<!DOCTYPE html>



  


<html class="theme-next mist use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<meta name="theme-color" content="#222">

<script>
    (function(){
        if('Android'){
            if (prompt('请输入文章密码') !== 'Android'){
                alert('密码错误！');
                history.back();
            }
        }
    })();
</script>








<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">






  <script>
  (function(i,s,o,g,r,a,m){i["DaoVoiceObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;a.charset="utf-8";m.parentNode.insertBefore(a,m)})(window,document,"script",('https:' == document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/6f697dad.js","daovoice")
  daovoice('init', {
      app_id: "6f697dad"
    });
  daovoice('update');
  </script>















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon.ico?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Developers,">





  <link rel="alternate" href="/atom.xml" title="达叔小生" type="application/atom+xml">






<meta name="description" content="用一切抱怨的时间去努力">
<meta name="keywords" content="Developers">
<meta property="og:type" content="article">
<meta property="og:title" content="Android应用基础知识">
<meta property="og:url" content="https://huangguangda.github.io/2018/05/01/1/index.html">
<meta property="og:site_name" content="达叔小生">
<meta property="og:description" content="用一切抱怨的时间去努力">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://huangguangda.github.io/images/37.jpg">
<meta property="og:updated_time" content="2018-05-08T06:36:20.508Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Android应用基础知识">
<meta name="twitter:description" content="用一切抱怨的时间去努力">
<meta name="twitter:image" content="https://huangguangda.github.io/images/37.jpg">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Mist',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":true,"onmobile":true},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://huangguangda.github.io/2018/05/01/1/">




<!-- 网页加载条 -->
<script src="https://neveryu.github.io/js/src/pace.min.js"></script>
  <title>Android应用基础知识 | 达叔小生</title>
  









  <!-- 音频播放 -->
<link rel="stylesheet" href="/dist/APlayer.min.css">
<div id="aplayer"></div>
<script type="text/javascript" src="/dist/APlayer.min.js"></script>
<script type="text/javascript" src="/dist/music.js"></script>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <a href="https://github.com/huangguangda" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewbox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">达叔小生</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <h1 class="site-subtitle" itemprop="description">爱好分享的码农</h1>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-首页">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-关于">
          <a href="/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br>
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-标签">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-分类">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-归档">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-书籍">
          <a href="/books" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-book"></i> <br>
            
            书籍
          </a>
        </li>
      
        
        <li class="menu-item menu-item-电影">
          <a href="/movies" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-chrome"></i> <br>
            
            电影
          </a>
        </li>
      
        
        <li class="menu-item menu-item-站点">
          <a href="/sitemap.xml" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-sitemap"></i> <br>
            
            站点
          </a>
        </li>
      
        
        <li class="menu-item menu-item-公益">
          <a href="/404/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-heartbeat"></i> <br>
            
            公益
          </a>
        </li>
      
        
        <li class="menu-item menu-item-课本">
          <a href="/book/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-apple"></i> <br>
            
            课本
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br>
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>


 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://huangguangda.github.io/2018/05/01/1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="达叔小生">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="达叔小生">
    </span>

    
      <header class="post-header">

        
        
          <h2 class="post-title" itemprop="name headline">Android应用基础知识</h2>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-05-01T15:50:00+08:00">
                2018-05-01
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Developers/" itemprop="url" rel="index">
                    <span itemprop="name">Developers</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          
            <span class="post-meta-divider">|</span>
            <span class="page-pv">本文总阅读量
            <span class="busuanzi-value" id="busuanzi_value_page_pv"></span>次
            </span>
          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  26,419
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  96
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <blockquote class="blockquote-center">用一切抱怨的时间去努力</blockquote>

<p><img src="/images/37.jpg" style="width: 100%;height: 100%"></p>
<a id="more"></a>
<p>来源：<a href="https://developer.android.google.cn/guide/" target="_blank" rel="noopener">https://developer.android.google.cn/guide/</a></p>
<h2 id="Android-简介"><a href="#Android-简介" class="headerlink" title="Android 简介"></a>Android 简介</h2><p>Android 提供了一个内容丰富的应用框架，支持您在 Java 语言环境中为移动设备开发创新应用和游戏。在左侧导航窗格列出的文档中，提供了有关如何使用各种 Android API 开发应用的详细信息。</p>
<p>如果您是 Android 应用开发新手，则需了解以下有关 Android 应用框架的基本概念，这一点至关重要：</p>
<p>应用提供多个入口点<br>Android 应用都是将各种可单独调用的不同组件加以组合开发而成。例如，组件可以是为用户界面提供一个屏幕的单个“Activity”，也可以是在后台独立执行工作的“服务”。</p>
<p>您可以使用 intent 从一个组件启动另一个组件。甚至，您还可以启动不同应用中的组件，例如，启动地图应用中的 Activity 以显示地址。此模式可为单个应用提供多个入口点，并使任何应用均能够像用户“默认设置”一样处理其他应用可能调用的操作。</p>
<p>应用可适应不同的设备<br>Android 提供了一个自适应应用框架，可用以为不同的设备配置提供独特的资源。例如，您可以针对不同的屏幕尺寸创建不同的 XML 布局文件，系统将根据当前设备的屏幕尺寸确定要应用的布局。</p>
<p>如有任何应用功能需要相机等特定的硬件，则可在运行时查询设备功能的可用性。如有必要，您还可以声明您的应用所必需的功能，使 Google Play 商店等应用市场不得在不支持这些功能的设备上安装您的应用。</p>
<h2 id="构建您的第一个应用"><a href="#构建您的第一个应用" class="headerlink" title="构建您的第一个应用"></a>构建您的第一个应用</h2><p>欢迎从事 Android 应用开发！</p>
<p>本课将介绍如何构建您的第一个 Android 应用。您将学习如何使用 Android Studio 创建 Android 项目和运行可调试版本的应用。您还将了解一些 Android 应用设计的基础知识，包括如何构建简单的界面和处理用户输入。</p>
<h2 id="创建-Android-项目"><a href="#创建-Android-项目" class="headerlink" title="创建 Android 项目"></a>创建 Android 项目</h2><p>本课将向您介绍如何使用 Android Studio 创建新的 Android 项目并说明该项目中的一些文件。</p>
<p>在 Welcome to Android Studio 窗口中，点击 Start a new Android Studio project。</p>
<p>或者如果您已打开项目，请选择 File &gt; New Project。</p>
<p>在 New Project 屏幕中，输入以下值：<br>Application Name：“My First App”<br>Company Domain：“example.com”<br>您可能需要更改项目位置，但无需更改其他选项。</p>
<p>点击 Next。</p>
<p>在 Target Android Devices 屏幕中，保留默认值并点击 Next。<br>在 Add an Activity to Mobile 屏幕中，选择 Empty Activity，然后点击 Next。<br>在 Configure Activity 屏幕中，保留默认值并点击 Finish。<br>经过一些处理后，Android Studio 将打开 IDE。</p>
<p>下面让我们花一点时间回顾一下最重要的文件。</p>
<p>首先，请确保已打开 Project 窗口（选择 View &gt; Tool Windows &gt; Project），并从窗口顶部的下拉列表中选择 Android 视图。随后，您可以看到下列文件：</p>
<p>app &gt; java &gt; com.example.myfirstapp &gt; MainActivity.java</p>
<p>这是主 Activity（您的应用的入口点）。当您构建和运行应用时，系统会启动此 Activity 的实例并加载其布局。</p>
<p>app &gt; res &gt; layout &gt; activity_main.xml</p>
<p>此 XML 文件会定义 Activity 界面的布局。它包含一个带有文本“Hello world!”的 TextView 元素。</p>
<p>app &gt; manifests &gt; AndroidManifest.xml</p>
<p>manifest 文件描述应用的基本特性并定义其每个组件。</p>
<p>Gradle Scripts &gt; build.gradle</p>
<p>您会看到具有此名称的两个文件：一个用于项目，一个用于“应用”模块。每个模块均有自己的 build.gradle 文件，但此项目当前仅有一个模块。您将主要使用模块的 build.gradle 文件配置 Gradle 工具编译和构建您的应用的方式。如需了解有关此文件的更多信息，请参阅配置构建。</p>
<h2 id="运行您的应用"><a href="#运行您的应用" class="headerlink" title="运行您的应用"></a>运行您的应用</h2><p>现在，您可以在真实设备或模拟器上运行应用。</p>
<p>在真实设备上运行</p>
<p>按照以下步骤设置您的设备：</p>
<p>使用一根 USB 电缆将您的设备连接到您的开发机器。如果您是在 Windows 上开发，可能需要为您的设备安装相应的 USB 驱动程序。</p>
<p>按照以下步骤操作，在 Developer options 中启用 USB debugging。<br>首先，您必须启用开发者选项：</p>
<p>打开 Settings 应用。<br>（仅在 Android 8.0 或更高版本上）选择 System。<br>滚动到底部，然后选择 About phone。<br>滚动到底部，点按 Build number 7 次。<br>返回上一屏幕，在底部附近可找到 Developer options。<br>打开 Developer options，然后向下滚动以找到并启用 USB debugging。</p>
<p>按照以下步骤操作，在您的设备上运行应用：</p>
<p>在 Android Studio 中，点击 Project 窗口中的 app 模块，然后选择 Run &gt; Run（或点击工具栏中的 Run  ）。<br>在 Select Deployment Target 窗口中，选择您的设备，然后点击 OK。<br>Android Studio 会在您连接的设备上安装并启动应用。</p>
<p>至此，“hello world”将在您的设备上运行！要开始开发应用，请继续学习下一课。</p>
<p>在模拟器上运行<br>按照以下步骤操作，在模拟器上运行应用：</p>
<p>在 Android Studio 中，点击 Project 窗口中的 app 模块，然后选择 Run &gt; Run（或点击工具栏中的 Run  ）。</p>
<p>在 Select Deployment Target 窗口中，点击 Create New Virtual Device。 </p>
<p>在 Select Hardware 屏幕中，选择电话设备（如 Pixel），然后点击 Next。</p>
<p>在 System Image 屏幕中，选择具有最高 API 级别的版本。如果您未安装该版本，将显示一个 Download 链接，因此，请点击该链接并完成下载。</p>
<p>点击 Next。</p>
<p>在 Android Virtual Device (AVD) 屏幕上，保留所有设置不变，然后点击 Finish。</p>
<p>返回到 Select Deployment Target 对话框中，选择您刚刚创建的设备，然后点击 OK。</p>
<p>Android Studio 会在模拟器上安装并启动应用。</p>
<h2 id="构建简单的界面"><a href="#构建简单的界面" class="headerlink" title="构建简单的界面"></a>构建简单的界面</h2><p>Android 应用的界面使用布局（ViewGroup 对象）和微件（View 对象）层次结构构建。布局是一种不可见的容器，用于控制其子视图在屏幕上的位置。微件是界面组件，例如按钮和文本框。</p>
<p>ViewGroup 对象如何在布局中形成分支并容纳 View 对象的图解</p>
<p>Android 为 ViewGroup 和 View 类提供了一个 XML 词汇，因此您的大多数界面都在 XML 文件中定义。</p>
<h2 id="打开布局编辑器"><a href="#打开布局编辑器" class="headerlink" title="打开布局编辑器"></a>打开布局编辑器</h2><p>首先，请按照以下步骤设置您的工作区：</p>
<p>在 Android Studio 的 Project 窗口中，打开 app &gt; res &gt; layout &gt; activity_main.xml。<br>要为布局编辑器留出更多空间，请选择 View &gt; Tool Windows &gt; Project 以隐藏 Project 窗口，或者点击 Android Studio 左侧的 Project  ）。<br>如果您的编辑器显示 XML 源代码，请点击窗口底部的 Design 标签。<br>点击 Select Design Surface  并选择 Blueprint。<br>点击工具栏中的 Show   并确保选中 Show Constraints。<br>确保 Autoconnect 关闭。工具栏中的提示应为 Turn On Autoconnect  （因为它现在处于关闭状态）。<br>点击工具栏中的 Default Margins，  然后选择 16（您稍后仍然可以调整每个视图的外边距）。<br>点击工具栏中的 Device in Editor，  然后选择 Pixel XL。</p>
<p>左下方的 Component Tree 窗口显示布局的视图层次结构。在本例中，根视图是 ConstraintLayout，仅包含一个 TextView 对象。</p>
<p>ConstraintLayout 是一种布局，它根据同级视图和父布局的约束条件为每个视图定义位置。这样一来，您可以创建具有扁平视图层次结构的简单布局和复杂布局。也就是说，它可以避免对嵌套布局（布局内的布局，如图 2 所示）的需求，嵌套布局会增加绘制界面所需的时间。</p>
<p>例如，您可以声明以下布局（在图 4 中）：</p>
<p>视图 A 显示在父布局上方 16dp 处。<br>视图 A 显示在父布局左侧 16dp 处。<br>视图 B 显示在视图 A 右侧 16dp 处。<br>视图 B 与视图 A 的顶部对齐。<br>在后面几部分中，您将构建一个与此布局类似的布局。</p>
<h2 id="添加一个文本框"><a href="#添加一个文本框" class="headerlink" title="添加一个文本框"></a>添加一个文本框</h2><p>首先，您需要移除布局中已有的内容。因此，请点击 Component Tree 窗口中的 TextView，然后按 Delete。</p>
<p>在左侧的 Palette 窗口中，点击左侧窗格中的 Text，然后将 Plain Text 拖放到设计编辑器中靠近布局顶部的位置。这是一个接受纯文本输入的 EditText 微件。</p>
<p>在设计编辑器中点击视图。现在，您可以在每个角上看到大小调整手柄（正方形），并在每个边上看到约束锚点（圆形）。</p>
<p>为了能更好地控制，您可能需要使用工具栏中的按钮放大编辑器。</p>
<p>点击并按住顶边上的锚点，将其向上拖动，直至锚点吸附到布局的顶部，然后释放。这是一个约束条件 - 它指定视图应位于距离布局顶部 16dp 的位置（因为您将默认外边距设置为 16dp）。<br>类似地，从视图左侧创建一个约束条件，将其限制在布局左侧。</p>
<h2 id="添加一个按钮"><a href="#添加一个按钮" class="headerlink" title="添加一个按钮"></a>添加一个按钮</h2><p>按钮被限制在文本框及其基线的右侧</p>
<p>在 Palette 窗口中，点击左侧窗格中的 Widgets，然后将 Button 拖放到设计编辑器中靠近右侧的位置。</p>
<p>从按钮左侧创建一个约束条件，将其限制在文本框的右侧。</p>
<p>要在水平对齐中约束视图，您需要在文本基线之间创建一个约束条件。因此，请点击按钮，然后点击 Edit Baseline， 它将显示在设计编辑器中选定视图的正下方。基线锚点显示在按钮内部。点击并按住此锚点，然后将其拖动到文本框中显示的基线锚点。</p>
<p>注：您还可以使用顶部或底部边缘创建水平对齐，不过，按钮在其图像周围包含内边距，因此如果您按照这种方式对齐这些视图，视觉对齐将是错误的。</p>
<h2 id="更改界面字符串"><a href="#更改界面字符串" class="headerlink" title="更改界面字符串"></a>更改界面字符串</h2><p>要预览界面，请点击工具栏中的 Select Design Surface  并选择 Design。请注意，文本输入使用“Name”预填充，按钮标记为“Button”。现在，您将更改这些字符串。</p>
<p>打开 Project 窗口，然后打开 app &gt; res &gt; values &gt; strings.xml。</p>
<p>这是一个字符串资源文件，您应在这个文件中指定所有界面字符串。这样您可以在一个位置管理所有界面字符串，让字符串的查找、更新和本地化变得更加容易（与您的布局或应用代码中的硬编码字符串相比）。</p>
<p>点击编辑器窗口顶部的 Open editor。这将打开 Translations Editor，它为添加和编辑您的默认字符串提供了一个简单的界面，并且有助于保持您的所有已翻译字符串井然有序。</p>
<h2 id="用于添加新字符串的对话框"><a href="#用于添加新字符串的对话框" class="headerlink" title="用于添加新字符串的对话框"></a>用于添加新字符串的对话框</h2><p>点击 Add Key，  以文本框“提示文本”的形式创建新字符串。<br>为键名输入“edit_message”。<br>为值输入“Enter a message”。<br>点击 OK。<br>再添加一个名称为“button_send”并且值为“Send”的键。</p>
<p>现在，您可以为每个视图设置这些字符串。点击标签栏中的 activity_main.xml 以返回布局文件，然后按照以下步骤添加字符串：</p>
<p>点击布局中的文本框，如果 Attributes 窗口在右侧还未显示，请点击右侧边栏上的 Attributes  。<br>找到 text 属性（当前设为“Name”）并删除值。<br>找到 hint 属性，然后点击文本框右侧的 Pick a Resource  。在出现的对话框中，双击列表中的 edit_message。<br>现在，点击布局中的按钮，找到 text 属性，点击 Pick a Resource，  然后选择 button_send。</p>
<h2 id="让文本框大小灵活调整"><a href="#让文本框大小灵活调整" class="headerlink" title="让文本框大小灵活调整"></a>让文本框大小灵活调整</h2><p>要创建一个可以适应不同屏幕尺寸的布局，您现在将让文本框拉伸以填充剩余的所有水平空间（扣除按钮和所有外边距后的空间）。</p>
<p>首先，请点击工具栏中的 Show  并选择 Blueprint。</p>
<p>点击 Center Horizontally 的结果</p>
<p>点击以将宽度更改为 Match Constraints</p>
<p>现在，文本框将拉伸以填充剩余空间</p>
<p>选择两个视图（点击一个，按住 Shift 并点击另一个），然后右键点击任何一个视图并选择 Chain &gt; Create Horizontal Chain。</p>
<p>链是两个或更多视图之间的双向约束条件，它让您可以采用一致的方式放置链接的视图。</p>
<p>选择按钮并打开 Attributes 窗口。使用 Attributes 窗口顶部的视图检查器将右外边距设置为 16。<br>现在，点击文本框以查看其属性。点击两次宽度指示器，确保将其设置为 Match Constraints，如图 9 中的标注 1 所示。<br>“Match constraints”表示宽度将延长以符合水平约束条件和外边距的定义。因此，文本框将拉伸以填充水平空间（扣除按钮和所有外边距后的空间）。</p>
<h2 id="运行应用"><a href="#运行应用" class="headerlink" title="运行应用"></a>运行应用</h2><p>如果已在上一课中将您的应用安装到设备上，只需点击工具栏中的 Apply Changes，  使用新布局更新应用。或者点击 Run，  安装并运行应用。</p>
<h2 id="启动另一个-Activity"><a href="#启动另一个-Activity" class="headerlink" title="启动另一个 Activity"></a>启动另一个 Activity</h2><h2 id="响应-Send-按钮"><a href="#响应-Send-按钮" class="headerlink" title="响应 Send 按钮"></a>响应 Send 按钮</h2><p>按照以下步骤操作，在 MainActivity.java 中添加一个由按钮调用的函数：</p>
<p>在文件 app &gt; java &gt; com.example.myfirstapp &gt; MainActivity.java 中，添加 sendMessage() 函数存根，如下所示：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">public class MainActivity extends AppCompatActivity &#123;</span><br><span class="line">    @Override</span><br><span class="line">    protected void onCreate(Bundle savedInstanceState) &#123;</span><br><span class="line">        super.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /** Called when the user taps the Send button */</span><br><span class="line">    public void sendMessage(View view) &#123;</span><br><span class="line">        // Do something in response to button</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>您可能会看到一条错误，因为 Android Studio 无法解析用作函数参数的 View 类。因此，请点击，将您的光标放置在 View 声明上，然后按 Alt + Enter（在 Mac 上，则按 Option + Return），执行快速修复。（如果出现一个菜单，请选择 Import class。）</p>
<p>现在，返回到 activity_main.xml 文件，从按钮调用此函数：<br>在布局编辑器中点击以选择按钮。<br>在 Attributes 窗口中，找到 onClick 属性并从下拉列表中选择 sendMessage [MainActivity]。</p>
<p>现在，当点按按钮时，系统将调用 sendMessage() 函数。</p>
<p>记下此函数中的详细信息，要让系统将此函数视为与 android:onClick 属性兼容，需要这些详细信息。具体来说，函数必须声明以下内容：</p>
<ul>
<li>公共访问</li>
<li>空返回值</li>
<li>以 View 作为唯一参数（它是之前点击的 View 对象）</li>
</ul>
<p>接下来，您需要填写此函数以读取文本字段的内容，并将该文本传递给另一个 Activity。</p>
<h2 id="构建一个-Intent"><a href="#构建一个-Intent" class="headerlink" title="构建一个 Intent"></a>构建一个 Intent</h2><p>Intent 是指在相互独立的组件（如两个 Activity）之间提供运行时绑定功能的对象。Intent 表示一个应用“执行某项操作的意向”。您可以将 intent 用于各种任务，但在本课中，您的 intent 用于启动另一个 Activity。</p>
<p>在 MainActivity.java 中，添加 EXTRA_MESSAGE 常量和 sendMessage() 代码，如此处所示：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">public class MainActivity extends AppCompatActivity &#123;</span><br><span class="line">    public static final String EXTRA_MESSAGE = &quot;com.example.myfirstapp.MESSAGE&quot;;</span><br><span class="line">    @Override</span><br><span class="line">    protected void onCreate(Bundle savedInstanceState) &#123;</span><br><span class="line">        super.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /** Called when the user taps the Send button */</span><br><span class="line">    public void sendMessage(View view) &#123;</span><br><span class="line">        Intent intent = new Intent(this, DisplayMessageActivity.class);</span><br><span class="line">        EditText editText = (EditText) findViewById(R.id.editText);</span><br><span class="line">        String message = editText.getText().toString();</span><br><span class="line">        intent.putExtra(EXTRA_MESSAGE, message);</span><br><span class="line">        startActivity(intent);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Android Studio 将再次遇到 Cannot resolve symbol 错误，因此，请按 Alt + Enter（在 Mac 上，则按 Option + Return）。您的导入应按如下所示方式结束：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">import android.content.Intent;</span><br><span class="line">import android.support.v7.app.AppCompatActivity;</span><br><span class="line">import android.os.Bundle;</span><br><span class="line">import android.view.View;</span><br><span class="line">import android.widget.EditText;</span><br></pre></td></tr></table></figure>
<p>DisplayMessageActivity 仍有错误，但没关系；您将在下一部分中修复该错误。</p>
<p>下面是 sendMessage() 中的操作：</p>
<p>Intent 构造函数采用两个参数：</p>
<p>Context 是第一个参数（之所以使用 this 是因为 Activity 类是 Context 的子类）</p>
<p>应用组件的 Class，系统应将 Intent（在本例中，为应启动的 Activity）传递至该类。</p>
<p>putExtra() 函数将 EditText 的值添加到 intent。Intent 能够以名为 extra 的键值对形式携带数据类型。您的键是一个公共常量 EXTRA_MESSAGE，因为下一个 Activity 将使用该键来检索文本值。为 intent extra 定义键时最好使用应用的软件包名称作为前缀。这可以确保在您的应用与其他应用交互时这些键始终保持唯一。</p>
<p>startActivity() 函数将启动 Intent 指定的 DisplayMessageActivity 实例。现在，您需要创建该类。</p>
<h2 id="创建第二个-Activity"><a href="#创建第二个-Activity" class="headerlink" title="创建第二个 Activity"></a>创建第二个 Activity</h2><p>在 Project 窗口中，右键点击 app 文件夹并选择 New &gt; Activity &gt; Empty Activity。</p>
<p>在 Configure Activity 窗口中，为 Activity Name 输入“DisplayMessageActivity”，然后点击 Finish（保留所有其他属性设置为默认值）。</p>
<p>Android Studio 会自动执行三项操作：</p>
<p>创建 DisplayMessageActivity.java 文件。</p>
<p>创建对应的 activity_display_message.xml 布局文件。</p>
<p>在 AndroidManifest.xml 中添加必需的 <activity> 元素。</activity></p>
<p>如果运行应用并在第一个 Activity 上点按按钮，将启动第二个 Activity，但它为空。这是因为第二个 Activity 使用模板提供的空布局。</p>
<h2 id="添加文本视图"><a href="#添加文本视图" class="headerlink" title="添加文本视图"></a>添加文本视图</h2><p>位于布局顶部中心的文本视图</p>
<p>新 Activity 包括一个空白的布局文件，因此，现在您需要在消息将要显示的位置添加一个文本视图。</p>
<p>打开文件 app &gt; res &gt; layout &gt; activity_display_message.xml。<br>点击工具栏中的 Turn On Autoconnect （Autoconnect 随后应启用）。</p>
<p>在 Palette 窗口中，点击 Text，然后将 TextView 拖动到布局中 - 将其放置在靠近布局顶部中心的位置，使其吸附到出现的垂直线上。Autoconnect 将添加左侧和右侧约束条件，在水平中心放置视图。</p>
<p>再从文本视图的顶部创建一个约束条件，将其限制在布局顶部</p>
<p>也可以在 Attributes 窗口中展开 textAppearance 并更改属性（例如 textSize 和 textColor），对文本样式进行一些调整。</p>
<p>显示消息<br>现在，您将修改第二个 Activity，以显示第一个 Activity 传递的消息。</p>
<p>在 DisplayMessageActivity.java 中，向 onCreate() 函数添加下列代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">protected void onCreate(Bundle savedInstanceState) &#123;</span><br><span class="line">    super.onCreate(savedInstanceState);</span><br><span class="line">    setContentView(R.layout.activity_display_message);</span><br><span class="line">    </span><br><span class="line">    // Get the Intent that started this activity and extract the string</span><br><span class="line">    Intent intent = getIntent();</span><br><span class="line">    String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);</span><br><span class="line"></span><br><span class="line">    // Capture the layout&apos;s TextView and set the string as its text</span><br><span class="line">    TextView textView = findViewById(R.id.textView);</span><br><span class="line">    textView.setText(message);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>按 Alt + Enter（在 Mac 上，则按 Option + Return）导入缺少的类。您的导入应按如下所示方式结束：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">import android.content.Intent;</span><br><span class="line">import android.support.v7.app.AppCompatActivity;</span><br><span class="line">import android.os.Bundle;</span><br><span class="line">import android.widget.TextView;</span><br></pre></td></tr></table></figure>
<h2 id="添加向上导航"><a href="#添加向上导航" class="headerlink" title="添加向上导航"></a>添加向上导航</h2><p>您的应用中不是主入口的每个屏幕（所有不是主屏幕的屏幕）都应提供导航，以便用户在应用栏中点按“向上”按钮后可以返回应用层次结构中的逻辑父屏幕。</p>
<p>您需要做的全部工作是在 AndroidManifest.xml 文件中声明哪个 Activity 是逻辑父项。因此，请打开 app &gt; manifests &gt; AndroidManifest.xml 处的文件，找到 DisplayMessageActivity 的 <activity> 标记，然后将其替换为以下代码：</activity></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&lt;activity android:name=&quot;.DisplayMessageActivity&quot;</span><br><span class="line">          android:parentActivityName=&quot;.MainActivity&quot; &gt;</span><br><span class="line">    &lt;!-- The meta-data tag is required if you support API level 15 and lower --&gt;</span><br><span class="line">    &lt;meta-data</span><br><span class="line">        android:name=&quot;android.support.PARENT_ACTIVITY&quot;</span><br><span class="line">        android:value=&quot;.MainActivity&quot; /&gt;</span><br><span class="line">&lt;/activity&gt;</span><br><span class="line">Android 系统现在会在应用栏中自动添加“向上”按钮。</span><br></pre></td></tr></table></figure>
<h2 id="运行应用-1"><a href="#运行应用-1" class="headerlink" title="运行应用"></a>运行应用</h2><p>现在，点击工具栏中的 Apply Changes，再次运行应用  。当应用打开后，在文本字段中键入一条消息，点按 Send 以在第二个 Activity 中查看显示的消息。</p>
<h2 id="应用基础知识"><a href="#应用基础知识" class="headerlink" title="应用基础知识"></a>应用基础知识</h2><p>Android 应用采用 Java 编程语言编写。Android SDK 工具将您的代码 — 连同任何数据和资源文件 — 编译到一个 APK：Android 软件包，即带有 .apk 后缀的存档文件中。一个 APK 文件包含 Android 应用的所有内容，它是基于 Android 系统的设备用来安装应用的文件。</p>
<p>安装到设备后，每个 Android 应用都运行在自己的安全沙箱内：</p>
<p>Android 操作系统是一种多用户 Linux 系统，其中的每个应用都是一个不同的用户；<br>默认情况下，系统会为每个应用分配一个唯一的 Linux 用户 ID（该 ID 仅由系统使用，应用并不知晓）。系统为应用中的所有文件设置权限，使得只有分配给该应用的用户 ID 才能访问这些文件；<br>每个进程都具有自己的虚拟机 (VM)，因此应用代码是在与其他应用隔离的环境中运行；<br>默认情况下，每个应用都在其自己的 Linux 进程内运行。Android 会在需要执行任何应用组件时启动该进程，然后在不再需要该进程或系统必须为其他应用恢复内存时关闭该进程。<br>Android 系统可以通过这种方式实现最小权限原则。也就是说，默认情况下，每个应用都只能访问执行其工作所需的组件，而不能访问其他组件。 这样便营造出一个非常安全的环境，在这个环境中，应用无法访问系统中其未获得权限的部分。</p>
<p>不过，应用仍然可以通过一些途径与其他应用共享数据以及访问系统服务：</p>
<p>可以安排两个应用共享同一 Linux 用户 ID，在这种情况下，它们能够相互访问彼此的文件。 为了节省系统资源，可以安排具有相同用户 ID 的应用在同一 Linux 进程中运行，并共享同一 VM（应用还必须使用相同的证书签署）。<br>应用可以请求访问设备数据（如用户的联系人、短信、可装载存储装置 [SD 卡]、相机、蓝牙等）的权限。 用户必须明确授予这些权限。 如需了解详细信息，请参阅 使用系统权限。<br>以上内容阐述了有关 Android 应用在系统内存在方式的基础知识。本文的其余部分将向您介绍以下内容：</p>
<p>用于定义应用的核心框架组件<br>您用来声明组件和应用必需设备功能的清单文件<br>与应用代码分离并允许您的应用针对各种设备配置适当优化其行为的资源<br>应用组件<br>应用组件是 Android 应用的基本构建基块。每个组件都是一个不同的点，系统可以通过它进入您的应用。 并非所有组件都是用户的实际入口点，有些组件相互依赖，但每个组件都以独立实体形式存在，并发挥特定作用 — 每个组件都是唯一的构建基块，有助于定义应用的总体行为。</p>
<p>共有四种不同的应用组件类型。每种类型都服务于不同的目的，并且具有定义组件的创建和销毁方式的不同生命周期。</p>
<p>以下便是这四种应用组件类型：</p>
<p>Activity<br>Activity 表示具有用户界面的单一屏幕。例如，电子邮件应用可能具有一个显示新电子邮件列表的 Activity、一个用于撰写电子邮件的 Activity 以及一个用于阅读电子邮件的 Activity。 尽管这些 Activity 通过协作在电子邮件应用中形成了一种紧密结合的用户体验，但每一个 Activity 都独立于其他 Activity 而存在。 因此，其他应用可以启动其中任何一个 Activity（如果电子邮件应用允许）。 例如，相机应用可以启动电子邮件应用内用于撰写新电子邮件的 Activity，以便用户共享图片。<br>Activity 作为 Activity 的子类实现，您可以在 Activity 开发者指南中了解有关它的更多详情。</p>
<p>服务<br>服务是一种在后台运行的组件，用于执行长时间运行的操作或为远程进程执行作业。 服务不提供用户界面。 例如，当用户位于其他应用中时，服务可能在后台播放音乐或者通过网络获取数据，但不会阻断用户与 Activity 的交互。 诸如 Activity 等其他组件可以启动服务，让其运行或与其绑定以便与其进行交互。<br>服务作为 Service 的子类实现，您可以在服务开发者指南中了解有关它的更多详情。</p>
<p>内容提供程序<br>内容提供程序管理一组共享的应用数据。您可以将数据存储在文件系统、SQLite 数据库、网络上或您的应用可以访问的任何其他永久性存储位置。 其他应用可以通过内容提供程序查询数据，甚至修改数据（如果内容提供程序允许）。 例如，Android 系统可提供管理用户联系人信息的内容提供程序。 因此，任何具有适当权限的应用都可以查询内容提供程序的某一部分（如 ContactsContract.Data），以读取和写入有关特定人员的信息。<br>内容提供程序也适用于读取和写入您的应用不共享的私有数据。 例如，记事本示例应用使用内容提供程序来保存笔记。</p>
<p>内容提供程序作为 ContentProvider 的子类实现，并且必须实现让其他应用能够执行事务的一组标准 API。 如需了解详细信息，请参阅内容提供程序开发者指南。</p>
<p>广播接收器<br>广播接收器是一种用于响应系统范围广播通知的组件。 许多广播都是由系统发起的 — 例如，通知屏幕已关闭、电池电量不足或已拍摄照片的广播。应用也可以发起广播 — 例如，通知其他应用某些数据已下载至设备，并且可供其使用。 尽管广播接收器不会显示用户界面，但它们可以创建状态栏通知，在发生广播事件时提醒用户。 但广播接收器更常见的用途只是作为通向其他组件的“通道”，设计用于执行极少量的工作。 例如，它可能会基于事件发起一项服务来执行某项工作。<br>广播接收器作为 BroadcastReceiver 的子类实现，并且每条广播都作为 Intent 对象进行传递。 如需了解详细信息，请参阅 BroadcastReceiver 类。</p>
<p>Android 系统设计的独特之处在于，任何应用都可以启动其他应用的组件。 例如，如果您想让用户使用设备的相机拍摄照片，很可能有另一个应用可以执行该操作，那么您的应用就可以利用该应用，而不是开发一个 Activity 来自行拍摄照片。 您不需要集成甚至链接到该相机应用的代码，而是只需启动拍摄照片的相机应用中的 Activity。 完成拍摄时，系统甚至会将照片返回您的应用，以便您使用。对用户而言，就好像相机真正是您应用的组成部分。</p>
<p>当系统启动某个组件时，会启动该应用的进程（如果尚未运行），并实例化该组件所需的类。 例如，如果您的应用启动相机应用中拍摄照片的 Activity，则该 Activity 会在属于相机应用的进程，而不是您的应用的进程中运行。因此，与大多数其他系统上的应用不同，Android 应用并没有单一入口点（例如，没有 main() 函数）。</p>
<p>由于系统在单独的进程中运行每个应用，且其文件权限会限制对其他应用的访问，因此您的应用无法直接启动其他应用中的组件， 但 Android 系统却可以。因此，要想启动其他应用中的组件，您必须向系统传递一则消息，说明您想启动特定组件的 Intent。 系统随后便会为您启动该组件。</p>
<p>启动组件<br>四种组件类型中的三种 — Activity、服务和广播接收器 — 通过名为 Intent 的异步消息进行启动。Intent 会在运行时将各个组件相互绑定（您可以将 Intent 视为从其他组件请求操作的信使），无论组件属于您的应用还是其他应用。</p>
<p>Intent 使用 Intent 对象创建，它定义的消息用于启动特定组件或特定类型的组件 — Intent 可以是显式的，也可以是隐式的。</p>
<p>对于 Activity 和服务， Intent 定义要执行的操作（例如，“查看”或“发送”某个内容），并且可以指定要执行操作的数据的 URI（以及正在启动的组件可能需要了解的信息）。 例如， Intent 传达的请求可以是启动一个显示图像或打开网页的 Activity。 在某些情况下，您可以启动 Activity 来接收结果，在这种情况下，Activity 也会在 Intent 中返回结果（例如，您可以发出一个 Intent，让用户选取某位联系人并将其返回给您 — 返回 Intent 包括指向所选联系人的 URI）。</p>
<p>对于广播接收器， Intent 只会定义要广播的通知（例如，指示设备电池电量不足的广播只包括指示“电池电量不足”的已知操作字符串）。</p>
<p>Intent 不会启动另一个组件类型 - 内容提供程序，后者会在成为 ContentResolver 的请求目标时启动。 内容解析程序通过内容提供程序处理所有直接事务，使得通过提供程序执行事务的组件可以无需执行事务，而是改为在 ContentResolver 对象上调用方法。 这会在内容提供程序与请求信息的组件之间留出一个抽象层（以确保安全）。</p>
<p>每种类型的组件有不同的启动方法：</p>
<p>您可以通过将 Intent 传递到 startActivity() 或 startActivityForResult()（当您想让 Activity 返回结果时）来启动 Activity（或为其安排新任务）。<br>您可以通过将　Intent 传递到 startService() 来启动服务（或对执行中的服务下达新指令）。 或者，您也可以通过将 Intent 传递到 bindService() 来绑定到该服务。<br>您可以通过将 Intent 传递到 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast() 等方法来发起广播；<br>您可以通过在 ContentResolver 上调用 query() 来对内容提供程序执行查询。<br>如需了解有关 Intent 用法的详细信息，请参阅 Intent 和 Intent 过滤器文档。 以下文档中还提供了有关启动特定组件的详细信息： Activity、服务、BroadcastReceiver 和内容提供程序。</p>
<p>清单文件<br>在 Android 系统启动应用组件之前，系统必须通过读取应用的 AndroidManifest.xml 文件（“清单”文件）确认组件存在。 您的应用必须在此文件中声明其所有组件，该文件必须位于应用项目目录的根目录中。</p>
<p>除了声明应用的组件外，清单文件还有许多其他作用，如：</p>
<p>确定应用需要的任何用户权限，如互联网访问权限或对用户联系人的读取权限<br>根据应用使用的 API，声明应用所需的最低 API 级别<br>声明应用使用或需要的硬件和软件功能，如相机、蓝牙服务或多点触摸屏幕<br>应用需要链接的 API 库（Android 框架 API 除外），如 Google 地图库<br>其他功能<br>声明组件<br>清单文件的主要任务是告知系统有关应用组件的信息。例如，清单文件可以像下面这样声明 Activity：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span><br><span class="line">&lt;manifest ... &gt;</span><br><span class="line">    &lt;application android:icon=&quot;@drawable/app_icon.png&quot; ... &gt;</span><br><span class="line">        &lt;activity android:name=&quot;com.example.project.ExampleActivity&quot;</span><br><span class="line">                  android:label=&quot;@string/example_label&quot; ... &gt;</span><br><span class="line">        &lt;/activity&gt;</span><br><span class="line">        ...</span><br><span class="line">    &lt;/application&gt;</span><br><span class="line">&lt;/manifest&gt;</span><br></pre></td></tr></table></figure></p>
<p>在 <application> 元素中，android:icon 属性指向标识应用的图标所对应的资源。</application></p>
<p>在 <activity> 元素中，android:name 属性指定 Activity 子类的完全限定类名，android:label 属性指定用作 Activity 的用户可见标签的字符串。</activity></p>
<p>您必须通过以下方式声明所有应用组件：</p>
<p>Activity 的 <activity> 元素<br>服务的 <service> 元素<br>广播接收器的 <receiver> 元素<br>内容提供程序的 <provider> 元素<br>您包括在源代码中，但未在清单文件中声明的 Activity、服务和内容提供程序对系统不可见，因此也永远不会运行。 不过，广播接收器可以在清单文件中声明或在代码中动态创建（如 BroadcastReceiver 对象）并通过调用 registerReceiver() 在系统中注册。</provider></receiver></service></activity></p>
<p>如需了解有关如何为您的应用构建清单文件的详细信息，请参阅 AndroidManifest.xml 文件文档。</p>
<p>声明组件功能<br>如上文启动组件中所述，您可以使用 Intent 来启动 Activity、服务和广播接收器。 您可以通过在 Intent 中显式命名目标组件（使用组件类名）来执行此操作。 不过，Intent 的真正强大之处在于隐式 Intent 概念。 隐式 Intent 的作用无非是描述要执行的操作类型（还可选择描述您想执行的操作所针对的数据），让系统能够在设备上找到可执行该操作的组件，并启动该组件。 如果有多个组件可以执行 Intent 所描述的操作，则由用户选择使用哪一个组件。</p>
<p>系统通过将接收到的 Intent 与设备上的其他应用的清单文件中提供的 Intent 过滤器进行比较来确定可以响应 Intent 的组件。</p>
<p>当您在应用的清单文件中声明 Activity 时，可以选择性地加入声明 Activity 功能的 Intent 过滤器，以便响应来自其他应用的 Intent。 您可以通过将 <intent-filter> 元素作为组件声明元素的子项进行添加来为您的组件声明 Intent 过滤器。</intent-filter></p>
<p>例如，如果您开发的电子邮件应用包含一个用于撰写新电子邮件的 Activity，则可以像下面这样声明一个 Intent 过滤器来响应“send” Intent（以发送新电子邮件）：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">&lt;manifest ... &gt;</span><br><span class="line">    ...</span><br><span class="line">    &lt;application ... &gt;</span><br><span class="line">        &lt;activity android:name=&quot;com.example.project.ComposeEmailActivity&quot;&gt;</span><br><span class="line">            &lt;intent-filter&gt;</span><br><span class="line">                &lt;action android:name=&quot;android.intent.action.SEND&quot; /&gt;</span><br><span class="line">                &lt;data android:type=&quot;*/*&quot; /&gt;</span><br><span class="line">                &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; /&gt;</span><br><span class="line">            &lt;/intent-filter&gt;</span><br><span class="line">        &lt;/activity&gt;</span><br><span class="line">    &lt;/application&gt;</span><br><span class="line">&lt;/manifest&gt;</span><br></pre></td></tr></table></figure></p>
<p>然后，如果另一个应用创建了一个包含ACTION_SEND 操作的 Intent，并将其传递到 startActivity()，则系统可能会启动您的 Activity，以便用户能够草拟并发送电子邮件。</p>
<p>如需了解有关创建 Intent 过滤器的详细信息，请参阅 Intent 和 Intent 过滤器文档。</p>
<p>声明应用要求<br>基于 Android 系统的设备多种多样，并非所有设备都提供相同的特性和功能。 为防止将您的应用安装在缺少应用所需特性的设备上，您必须通过在清单文件中声明设备和软件要求，为您的应用支持的设备类型明确定义一个配置文件。 其中的大多数声明只是为了提供信息，系统不会读取它们，但 Google Play 等外部服务会读取它们，以便当用户在其设备中搜索应用时为用户提供过滤功能。</p>
<p>例如，如果您的应用需要相机，并使用 Android 2.1（API 级别 7）中引入的 API，您应该像下面这样在清单文件中以要求形式声明这些信息：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;manifest ... &gt;</span><br><span class="line">    &lt;uses-feature android:name=&quot;android.hardware.camera.any&quot;</span><br><span class="line">                  android:required=&quot;true&quot; /&gt;</span><br><span class="line">    &lt;uses-sdk android:minSdkVersion=&quot;7&quot; android:targetSdkVersion=&quot;19&quot; /&gt;</span><br><span class="line">    ...</span><br><span class="line">&lt;/manifest&gt;</span><br></pre></td></tr></table></figure></p>
<p>现在，没有相机且 Android 版本低于 2.1 的设备将无法从 Google Play 安装您的应用。</p>
<p>不过，您也可以声明您的应用使用相机，但并不要求必须使用。 在这种情况下，您的应用必须将 required 属性设置为 “false”，并在运行时检查设备是否具有相机，然后根据需要停用任何相机功能。</p>
<p>设备兼容性文档中提供了有关如何管理应用与不同设备兼容性的详细信息。</p>
<p>应用资源<br>Android 应用并非只包含代码 — 它还需要与源代码分离的资源，如图像、音频文件以及任何与应用的视觉呈现有关的内容。 例如，您应该通过 XML 文件定义 Activity 用户界面的动画、菜单、样式、颜色和布局。 使用应用资源能够在不修改代码的情况下轻松地更新应用的各种特性，并可通过提供备用资源集让您能够针对各种设备配置（如不同的语言和屏幕尺寸）优化您的应用。</p>
<p>对于您的 Android 项目中包括的每一项资源，SDK 构建工具都会定义一个唯一的整型 ID，您可以利用它来引用应用代码或 XML 中定义的其他资源中的资源。 例如，如果您的应用包含一个名为 logo.png 的图像文件（保存在 res/drawable/ 目录中），则 SDK 工具会生成一个名为 R.drawable.logo 的资源 ID，您可以利用它来引用该图像并将其插入您的用户界面。</p>
<p>提供与源代码分离的资源的其中一个最重要优点在于，您可以提供针对不同设备配置的备用资源。 例如，通过在 XML 中定义 UI 字符串，您可以将字符串翻译为其他语言，并将这些字符串保存在单独的文件中。 然后，Android 系统会根据向资源目录名称追加的语言限定符（如为法语字符串值追加 res/values-fr/）和用户的语言设置，对您的 UI 应用相应的语言字符串。</p>
<p>Android 支持许多不同的备用资源限定符。限定符是一种加入到资源目录名称中，用来定义这些资源适用的设备配置的简短字符串。 再举一例，您应该经常会根据设备的屏幕方向和尺寸为 Activity 创建不同的布局。 例如，当设备屏幕为纵向（长型）时，您可能想要一种垂直排列按钮的布局；但当屏幕为横向（宽型）时，应按水平方向排列按钮。 要想根据方向更改布局，您可以定义两种不同的布局，然后对每个布局的目录名称应用相应的限定符。 然后，系统会根据当前设备方向自动应用相应的布局。</p>
<h2 id="应用资料"><a href="#应用资料" class="headerlink" title="应用资料"></a>应用资料</h2><p>提供资源<br>您应该始终外部化应用资源，例如图像和代码中的字符串，这样有利于您单独维护这些资源。 此外，您还应该为特定设备配置提供备用资源，方法是将它们分组到专门命名的资源目录中。 在运行时，Android 会根据当前配置使用适当的资源。例如，您可能需要根据屏幕尺寸提供不同的 UI 布局，或者根据语言设置提供不同的字符串。</p>
<p>外部化应用资源后，即可使用在项目 R 类中生成的资源 ID 访问这些资源。有关如何在应用中使用资源，我们将在访问资源中讨论。 本文档介绍如何对 Android 项目中的资源进行分组，以及如何为特定的设备配置提供备用资源。</p>
<p>分组资源类型<br>您应将各种资源放入项目 res/ 目录的特定子目录下。例如，以下是一个简单项目的文件层次结构：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">MyProject/</span><br><span class="line">    src/  </span><br><span class="line">        MyActivity.java  </span><br><span class="line">    res/</span><br><span class="line">        drawable/  </span><br><span class="line">            graphic.png  </span><br><span class="line">        layout/  </span><br><span class="line">            main.xml</span><br><span class="line">            info.xml</span><br><span class="line">        mipmap/  </span><br><span class="line">            icon.png </span><br><span class="line">        values/  </span><br><span class="line">            strings.xml</span><br></pre></td></tr></table></figure></p>
<p>正如您在此示例中所看到的那样，res/ 目录包含所有资源（在子目录下）：一个图像资源、两个布局资源、启动器图标的 mipmap/ 目录以及一个字符串资源文件。资源目录名称非常重要，将在表 1 中进行介绍。</p>
<p>注：如需了解有关使用 mipmap 文件夹的详细信息，请参阅管理项目概览。</p>
<p>表 1. 项目 res/ 目录内支持的资源目录。</p>
<p>目录    资源类型<br>animator/    用于定义属性动画的 XML 文件。<br>anim/    定义渐变动画的 XML 文件。（属性动画也可以保存在此目录中，但是为了区分这两种类型，属性动画首选 animator/ 目录。）<br>color/    用于定义颜色状态列表的 XML 文件。请参阅颜色状态列表资源<br>drawable/<br>位图文件（.png、.9.png、.jpg、.gif）或编译为以下可绘制对象资源子类型的 XML 文件：</p>
<p>位图文件<br>九宫格（可调整大小的位图）<br>状态列表<br>形状<br>动画可绘制对象<br>其他可绘制对象<br>请参阅 可绘制对象资源。</p>
<p>mipmap/    适用于不同启动器图标密度的可绘制对象文件。如需了解有关使用 mipmap/ 文件夹管理启动器图标的详细信息，请参阅管理项目概览。<br>layout/    用于定义用户界面布局的 XML 文件。 请参阅布局资源。<br>menu/    用于定义应用菜单（如选项菜单、上下文菜单或子菜单）的 XML 文件。请参阅菜单资源。<br>raw/<br>要以原始形式保存的任意文件。要使用原始 InputStream 打开这些资源，请使用资源 ID（即 R.raw.filename）调用 Resources.openRawResource()。</p>
<p>但是，如需访问原始文件名和文件层次结构，则可以考虑将某些资源保存在 assets/ 目录下（而不是 res/raw/）。assets/ 中的文件没有资源 ID，因此您只能使用 AssetManager 读取这些文件。</p>
<p>values/<br>包含字符串、整型数和颜色等简单值的 XML 文件。</p>
<p>其他 res/ 子目录中的 XML 资源文件是根据 XML 文件名定义单个资源，而 values/ 目录中的文件可描述多个资源。对于此目录中的文件，<resources> 元素的每个子元素均定义一个资源。例如，<string> 元素创建 R.string 资源，<color> 元素创建 R.color 资源。</color></string></resources></p>
<p>由于每个资源均用其自己的 XML 元素定义，因此您可以根据自己的需要命名文件，并将不同的资源类型放在一个文件中。但是，为了清晰起见，您可能需要将独特的资源类型放在不同的文件中。 例如，对于可在此目录中创建的资源，下面给出了相应的文件名约定：</p>
<p>arrays.xml，用于资源数组（类型化数组）。<br>colors.xml：颜色值。<br>dimens.xml：尺寸值。<br>strings.xml：字符串值。<br>styles.xml：样式。<br>请参阅字符串资源、样式资源和更多资源类型。</p>
<p>xml/    可以在运行时通过调用 Resources.getXML() 读取的任意 XML 文件。各种 XML 配置文件（如可搜索配置）都必须保存在此处。<br>注意：切勿将资源文件直接保存在 res/ 目录内，这会导致出现编译错误。</p>
<p>如需了解有关某些资源类型的详细信息，请参阅资源类型文档。</p>
<p>保存在表 1 中定义的子目录下的资源是“默认”资源。即，这些资源定义应用的默认设计和内容。但是，采用 Android 技术的不同设备类型可能需要不同类型的资源。例如，如果设备的屏幕尺寸大于标准屏幕，则应提供不同的布局资源，以充分利用额外的屏幕空间。 或者，如果设备的语言设置不同，则应提供不同的字符串资源，以转换用户界面中的文本。 要为不同的设备配置提供这些不同资源，除了默认资源以外，您还需要提供备用资源。</p>
<p>提供备用资源</p>
<p>图 1. 两种不同的设备，均使用不同的布局资源。</p>
<p>几乎每个应用都应提供备用资源以支持特定的设备配置。 例如，对于不同的屏幕密度和语言，您应分别包括备用可绘制对象资源和备用字符串资源。 在运行时，Android 会检测当前设备配置并为应用加载合适的资源。</p>
<p>为一组资源指定特定于配置的备用资源：</p>
<p>在 res/ 中创建一个以 &lt;resources_name&gt;-&lt;config_qualifier&gt; 形式命名的新目录。</p>
<p>&lt;resources_name&gt; 是相应默认资源的目录名称（如表 1 中所定义）。</p>
<p><qualifier> 是指定要使用这些资源的各个配置的名称（如表 2 中所定义）。<br>您可以追加多个 <qualifier>。以短划线将其分隔。</qualifier></qualifier></p>
<p>注意：追加多个限定符时，必须按照表 2 中列出的相同顺序放置它们。如果限定符的顺序错误，则该资源将被忽略。</p>
<p>将相应的备用资源保存在此新目录下。这些资源文件的名称必须与默认资源文件完全一样。<br>例如，以下是一些默认资源和备用资源：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">res/</span><br><span class="line">    drawable/   </span><br><span class="line">        icon.png</span><br><span class="line">        background.png    </span><br><span class="line">    drawable-hdpi/  </span><br><span class="line">        icon.png</span><br><span class="line">        background.png</span><br></pre></td></tr></table></figure></p>
<p>hdpi 限定符表示该目录中的资源适用于屏幕密度较高的设备。其中每个可绘制对象目录中的图像已针对特定的屏幕密度调整大小，但是文件名完全相同。 这样一来，用于引用 icon.png 或 background.png 图像的资源 ID 始终相同，但是 Android 会通过将设备配置信息与资源目录名称中的限定符进行比较，选择最符合当前设备的各个资源版本。</p>
<p>Android 支持若干配置限定符，您可以通过使用短划线分隔每个限定符，向一个目录名称添加多个限定符。表 2 按优先顺序列出了有效的配置限定符；如果对资源目录使用多个限定符，则必须按照表中列出的顺序将它们添加到目录名称。</p>
<p>表 2. 配置限定符名称。</p>
<p>配置    限定符值    说明<br>MCC 和 MNC    示例：<br>mcc310<br>mcc310-mnc004<br>mcc208-mnc00<br>等等<br>移动国家代码 (MCC)，（可选）后跟设备 SIM 卡中的移动网络代码 (MNC)。例如，mcc310 是指美国的任一运营商，mcc310-mnc004 是指美国的 Verizon 公司，mcc208-mnc00 是指法国的 Orange 公司。</p>
<p>如果设备使用无线电连接（GSM 手机），则 MCC 和 MNC 值来自 SIM 卡。</p>
<p>也可以单独使用 MCC（例如，将国家/地区特定的合法资源包括在应用中）。如果只需根据语言指定，则改用“语言和区域”限定符（稍后进行介绍）。 如果决定使用 MCC 和 MNC 限定符，请谨慎执行此操作并测试限定符是否按预期工作。</p>
<p>另请参阅配置字段 mcc 和 mnc，这两个字段分别表示当前的移动国家代码和移动网络代码。</p>
<p>语言和区域    示例：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">en</span><br><span class="line">fr</span><br><span class="line">en-rUS</span><br><span class="line">fr-rFR</span><br><span class="line">fr-rCA</span><br><span class="line">等等</span><br></pre></td></tr></table></figure></p>
<p>语言通过由两个字母组成的 ISO 639-1 语言代码定义，可以选择后跟两个字母组成的 ISO 3166-1-alpha-2 区域码（前带小写字母“r”）。</p>
<p>这些代码不区分大小写；r 前缀用于区分区域码。 不能单独指定区域。</p>
<p>如果用户更改系统设置中的语言，它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响，请参阅处理运行时变更。</p>
<p>有关针对其他语言本地化应用的完整指南，请参阅本地化。</p>
<p>另请参阅 locale 配置字段，该字段表示当前的语言区域。</p>
<p>布局方向    ldrtl<br>ldltr<br>应用的布局方向。ldrtl 是指“布局方向从右到左”。ldltr 是指“布局方向从左到右”，这是默认的隐式值。</p>
<p>它适用于布局、图片或值等任何资源。</p>
<p>例如，若要针对阿拉伯语提供某种特定布局，并针对任何其他“从右到左”语言（如波斯语或希伯来语）提供某种通用布局，则可编码如下：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">res/</span><br><span class="line">    layout/   </span><br><span class="line">        main.xml  (Default layout)</span><br><span class="line">    layout-ar/  </span><br><span class="line">        main.xml  (Specific layout for Arabic)</span><br><span class="line">    layout-ldrtl/  </span><br><span class="line">        main.xml  (Any &quot;right-to-left&quot; language, except</span><br><span class="line">                  for Arabic, because the &quot;ar&quot; language qualifier</span><br><span class="line">                  has a higher precedence.)</span><br></pre></td></tr></table></figure></p>
<p>注：要为应用启用从右到左的布局功能，必须将 supportsRtl 设置为 “true”，并将 targetSdkVersion 设置为 17 或更高版本。</p>
<p>此项为 API 级别 17 中新增配置。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smallestWidth	sw&lt;N&gt;dp</span><br></pre></td></tr></table></figure></p>
<p>示例：<br>sw320dp<br>sw600dp<br>sw720dp<br>等等<br>屏幕的基本尺寸，由可用屏幕区域的最小尺寸指定。 具体来说，设备的 smallestWidth 是屏幕可用高度和宽度的最小尺寸（您也可以将其视为屏幕的“最小可能宽度”）。无论屏幕的当前方向如何，您均可使用此限定符确保应用 UI 的可用宽度至少为 <n>dp。</n></p>
<p>例如，如果布局要求屏幕区域的最小尺寸始终至少为 600dp，则可使用此限定符创建布局资源 res/layout-sw600dp/。仅当可用屏幕的最小尺寸至少为 600dp 时，系统才会使用这些资源，而不考虑 600dp 所代表的边是用户所认为的高度还是宽度。smallestWidth 是设备的固定屏幕尺寸特性；设备的 smallestWidth 不会随屏幕方向的变化而改变。</p>
<p>设备的 smallestWidth 将屏幕装饰元素和系统 UI 考虑在内。例如，如果设备的屏幕上有一些永久性 UI 元素占据沿 smallestWidth 轴的空间，则系统会声明 smallestWidth 小于实际屏幕尺寸，因为这些屏幕像素不适用于您的 UI。 因此，使用的值应该是布局所需要的实际最小尺寸（通常，无论屏幕的当前方向如何，此值都是布局支持的“最小宽度”）。</p>
<p>以下是一些可用于普通屏幕尺寸的值：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">320，适用于屏幕配置如下的设备：</span><br><span class="line">240x320 ldpi（QVGA 手机）</span><br><span class="line">320x480 mdpi（手机）</span><br><span class="line">480x800 hdpi（高密度手机）</span><br><span class="line">480，适用于 480x800 mdpi 之类的屏幕（平板电脑/手机）。</span><br><span class="line">600，适用于 600x1024 mdpi 之类的屏幕（7 英寸平板电脑）。</span><br><span class="line">720，适用于 720x1280 mdpi 之类的屏幕（10 英寸平板电脑）。</span><br><span class="line">应用为多个资源目录提供不同的 smallestWidth 限定符值时，系统会使用最接近（但未超出）设备 smallestWidth 的值。</span><br></pre></td></tr></table></figure></p>
<p>此项为 API 级别 13 中新增配置。</p>
<p>另请参阅 android:requiresSmallestWidthDp 属性和 smallestScreenWidthDp 配置字段，前者声明与应用兼容的最小 smallestWidth；后者存放设备的 smallestWidth 值。</p>
<p>如需了解有关设计不同屏幕和使用此限定符的详细信息，请参阅支持多种屏幕开发者指南。</p>
<p>可用宽度    w<n>dp</n></p>
<p>示例：<br>w720dp<br>w1024dp<br>等等<br>指定资源应该使用的最小可用屏幕宽度，以 dp 为单位，由 <n> 值定义。在横向和纵向之间切换时，为了匹配当前实际宽度，此配置值也会随之发生变化。</n></p>
<p>应用为多个资源目录提供不同的此配置值时，系统会使用最接近（但未超出）设备当前屏幕宽度的值。 此处的值考虑到了屏幕装饰元素，因此如果设备显示屏的左边缘或右边缘上有一些永久性 UI 元素，考虑到这些 UI 元素，它会使用小于实际屏幕尺寸的宽度值，这样会减少应用的可用空间。</p>
<p>此项为 API 级别 13 中新增配置。</p>
<p>另请参阅 screenWidthDp 配置字段，该字段存放当前屏幕宽度。</p>
<p>如需了解有关设计不同屏幕和使用此限定符的详细信息，请参阅支持多种屏幕开发者指南。</p>
<p>可用高度    h<n>dp</n></p>
<p>示例：<br>h720dp<br>h1024dp<br>等等<br>指定资源应该使用的最小可用屏幕高度，以“dp”为单位，由 <n> 值定义。 在横向和纵向之间切换时，为了匹配当前实际高度，此配置值也会随之发生变化。</n></p>
<p>应用为多个资源目录提供不同的此配置值时，系统会使用最接近（但未超出）设备当前屏幕高度的值。 此处的值考虑到了屏幕装饰元素，因此如果设备显示屏的上边缘或下边缘有一些永久性 UI 元素，考虑到这些 UI 元素，同时为减少应用的可用空间，它会使用小于实际屏幕尺寸的高度值。 非固定的屏幕装饰元素（例如，全屏时可隐藏的手机状态栏）并不在考虑范围内，标题栏或操作栏等窗口装饰也不在考虑范围内，因此应用必须准备好处理稍小于其所指定值的空间。</p>
<p>此项为 API 级别 13 中新增配置。</p>
<p>另请参阅 screenHeightDp 配置字段，该字段存放当前屏幕宽度。</p>
<p>如需了解有关设计不同屏幕和使用此限定符的详细信息，请参阅支持多种屏幕开发者指南。</p>
<p>屏幕尺寸    small<br>normal<br>large<br>xlarge<br>small：尺寸类似于低密度 QVGA 屏幕的屏幕。小屏幕的最小布局尺寸约为 320x426 dp 单位。例如，QVGA 低密度屏幕和 VGA 高密度屏幕。<br>normal：尺寸类似于中等密度 HVGA 屏幕的屏幕。标准屏幕的最小布局尺寸约为 320x470 dp 单位。例如，WQVGA 低密度屏幕、HVGA 中等密度屏幕、WVGA 高密度屏幕。<br>large：尺寸类似于中等密度 VGA 屏幕的屏幕。 大屏幕的最小布局尺寸约为 480x640 dp 单位。 例如，VGA 和 WVGA 中等密度屏幕。<br>xlarge：明显大于传统中等密度 HVGA 屏幕的屏幕。超大屏幕的最小布局尺寸约为 720x960 dp 单位。在大多数情况下，屏幕超大的设备体积过大，不能放进口袋，最常见的是平板式设备。 API 级别 9 中的新增配置。<br>注：使用尺寸限定符并不表示资源仅适用于该尺寸的屏幕。 如果没有为备用资源提供最符合当前设备配置的限定符，则系统可能使用其中最匹配的资源。</p>
<p>注意：如果所有资源均使用大于当前屏幕的尺寸限定符，则系统不会使用这些资源，并且应用在运行时将会崩溃（例如，如果所有布局资源均用 xlarge 限定符标记，但设备是标准尺寸的屏幕）。</p>
<p>此项为 API 级别 4 中新增配置。</p>
<p>如需了解详细信息，请参阅支持多种屏幕。</p>
<p>另请参阅 screenLayout 配置字段，该字段表示屏幕是小尺寸、标准尺寸还是大尺寸。</p>
<p>屏幕纵横比    long<br>notlong<br>long：宽屏，如 WQVGA、WVGA、FWVGA<br>notlong：非宽屏，如 QVGA、HVGA 和 VGA<br>此项为 API 级别 4 中新增配置。</p>
<p>它完全基于屏幕的纵横比（宽屏较宽），而与屏幕方向无关。</p>
<p>另请参阅 screenLayout 配置字段，该字段指示屏幕是否为宽屏。</p>
<p>圆形屏幕    round<br>notround<br>round：圆形屏幕，例如圆形可穿戴式设备<br>notround：方形屏幕，例如手机或平板电脑<br>此项为 API 级别 23 中新增配置。</p>
<p>另请参阅 isScreenRound() 配置方法，其指示屏幕是否为宽屏。</p>
<p>屏幕方向    port<br>land<br>port：设备处于纵向（垂直）<br>land：设备处于横向（水平）<br>如果用户旋转屏幕，它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响，请参阅处理运行时变更。</p>
<p>另请参阅 orientation 配置字段，该字段指示当前的设备方向。</p>
<p>UI 模式    car<br>desk<br>television<br>appliance watch<br>car：设备正在车载手机座上显示<br>desk：设备正在桌面手机座上显示<br>television：设备正在电视上显示，为用户提供“十英尺”体验，其 UI 位于远离用户的大屏幕上，主要面向方向键或其他非指针式交互<br>appliance：设备用作不带显示屏的装置<br>watch：设备配有显示屏，戴在手腕上<br>此项为 API 级别 8 中新增配置，API 13 中新增电视配置，API 20 中新增手表配置。</p>
<p>如需了解应用在设备插入手机座或从中移除时的响应方式，请阅读确定并监控插接状态和类型。</p>
<p>如果用户将设备放入手机座中，它有可能在应用生命周期中发生改变。 可以使用 UiModeManager 启用或禁用其中某些模式。如需了解这会在运行期间给应用带来哪些影响，请参阅处理运行时变更。</p>
<p>夜间模式    night<br>notnight<br>night：夜间<br>notnight：白天<br>此项为 API 级别 8 中新增配置。</p>
<p>如果夜间模式停留在自动模式（默认），它有可能在应用生命周期中发生改变。在这种情况下，该模式会根据当天的时间进行调整。 可以使用 UiModeManager 启用或禁用此模式。如需了解这会在运行期间给应用带来哪些影响，请参阅处理运行时变更。</p>
<p>屏幕像素密度 (dpi)    ldpi<br>mdpi<br>hdpi<br>xhdpi<br>xxhdpi<br>xxxhdpi<br>nodpi<br>tvdpi<br>anydpi<br>ldpi：低密度屏幕；约为 120dpi。<br>mdpi：中等密度（传统 HVGA）屏幕；约为 160dpi。<br>hdpi：高密度屏幕；约为 240dpi。<br>xhdpi：超高密度屏幕；约为 320dpi。此项为 API 级别 8 中新增配置<br>xxhdpi：超超高密度屏幕；约为 480dpi。此项为 API 级别 16 中新增配置<br>xxxhdpi：超超超高密度屏幕使用（仅限启动器图标，请参阅“支持多种屏幕”中的注释）；约为 640dpi。 此项为 API 级别 18 中新增配置<br>nodpi：它可用于您不希望缩放以匹配设备密度的位图资源。<br>tvdpi：密度介于 mdpi 和 hdpi 之间的屏幕；约为 213dpi。它并不是“主要”密度组， 主要用于电视，而大多数应用都不需要它。对于大多数应用而言，提供 mdpi 和 hdpi 资源便已足够，系统将根据需要对其进行缩放。此项为 API 级别 13 中新增配置<br>anydpi：此限定符适合所有屏幕密度，其优先级高于其他限定符。 这对于矢量可绘制对象很有用。 此项为 API 级别 21 中新增配置<br>六个主要密度之间的缩放比为 3:4:6:8:12:16（忽略 tvdpi 密度）。因此，9x9 (ldpi) 位图相当于 12x12 (mdpi)、18x18 (hdpi)、24x24 (xhdpi) 位图，依此类推。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">如果您认为图像资源在电视或其他某些设备上呈现的效果不够好，而想尝试使用 tvdpi 资源，则缩放比例为 1.33*mdpi。例如，mdpi 屏幕的 100px x 100px 图像应该相当于 tvdpi 的133px x 133px。</span><br><span class="line"></span><br><span class="line">注：使用密度限定符并不表示资源仅适用于该密度的屏幕。 如果没有为备用资源提供最符合当前设备配置的限定符，则系统可能使用其中最匹配的资源。</span><br></pre></td></tr></table></figure></p>
<p>如需了解有关如何处理不同屏幕密度以及 Android 如何缩放位图以适应当前密度的详细信息，请参阅支持多种屏幕。</p>
<p>触摸屏类型    notouch<br>finger<br>notouch：设备没有触摸屏。<br>finger：设备有一个专供用户通过手指直接与其交互的触摸屏。<br>另请参阅 touchscreen 配置字段，该字段指示设备上的触摸屏类型。</p>
<p>键盘可用性    keysexposed<br>keyshidden<br>keyssoft<br>keysexposed：设备具有可用的键盘。如果设备启用了软键盘（不无可能），那么即使硬键盘没有展示给用户，哪怕设备没有硬键盘，也可以使用此限定符。 如果没有提供或已经禁用软键盘，则只有在显示硬键盘时才会使用此限定符。<br>keyshidden：设备具有可用的硬键盘，但它处于隐藏状态，且设备没有启用软键盘。<br>keyssoft：设备已经启用软键盘（无论是否可见）。<br>如果提供了 keysexposed 资源，但未提供 keyssoft 资源，那么只要系统已经启用软键盘，就会使用 keysexposed 资源，而不考虑键盘是否可见。</p>
<p>如果用户打开硬键盘，它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响，请参阅处理运行时变更。</p>
<p>另请参阅配置字段 hardKeyboardHidden 和 keyboardHidden，这两个字段分别指示硬键盘的可见性和任何一种键盘（包括软键盘）的可见性。</p>
<p>主要文本输入法    nokeys<br>qwerty<br>12key<br>nokeys：设备没有用于文本输入的硬按键。<br>qwerty：设备具有标准硬键盘（无论是否对用户可见）。<br>12key：设备具有 12 键硬键盘（无论是否对用户可见）。<br>另请参阅 keyboard 配置字段，该字段指示可用的主要文本输入法。</p>
<p>导航键可用性    navexposed<br>navhidden<br>navexposed：导航键可供用户使用。<br>navhidden：导航键不可用（例如，位于密封盖子后面）。<br>如果用户显示导航键，它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响，请参阅处理运行时变更。</p>
<p>另请参阅 navigationHidden 配置字段，该字段指示导航键是否处于隐藏状态。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">主要非触摸导航方法	nonav</span><br><span class="line">dpad</span><br><span class="line">trackball</span><br><span class="line">wheel	</span><br><span class="line">nonav：除了使用触摸屏以外，设备没有其他导航设施。</span><br><span class="line">dpad：设备具有用于导航的方向键。</span><br><span class="line">trackball：设备具有用于导航的轨迹球。</span><br><span class="line">wheel：设备具有用于导航的方向盘（不常见）。</span><br><span class="line">另请参阅 navigation 配置字段，该字段指示可用的导航方法类型。</span><br></pre></td></tr></table></figure></p>
<p>平台版本（API 级别）    示例：<br>v3<br>v4<br>v7<br>等等<br>设备支持的 API 级别。例如，v1 对应于 API 级别 1（带有 Android 1.0 或更高版本系统的设备），v4 对应于 API 级别 4（带有 Android 1.6 或更高版本系统的设备）。如需了解有关这些值的详细信息，请参阅 Android API 级别文档。</p>
<p>注：有些配置限定符是从 Android 1.0 才开始添加，因此并非所有版本的 Android 系统都支持所有限定符。使用新限定符会隐式添加平台版本限定符，因此较旧版本系统的设备必然会忽略它。 例如，使用 w600dp 限定符会自动包括 v13 限定符，因为可用宽度限定符是 API 级别 13 中的新增配置。为了避免出现任何问题，请始终包含一组默认资源（一组“不带限定符”的资源）。 如需了解详细信息，请参阅利用资源提供最佳设备兼容性部分。</p>
<p>限定符命名规则<br>以下是一些关于使用配置限定符名称的规则：</p>
<p>您可以为单组资源指定多个限定符，并使用短划线分隔。例如，drawable-en-rUS-land 适用于横排美国英语设备。<br>这些限定符必须遵循表 2 中列出的顺序。例如：<br>错误：drawable-hdpi-port/<br>正确：drawable-port-hdpi/<br>不能嵌套备用资源目录。例如，您不能拥有 res/drawable/drawable-en/。<br>值不区分大小写。在处理之前，资源编译器会将目录名称转换为小写，以避免不区分大小写的文件系统出现问题。 名称中使用的任何大写字母只是为了便于认读。<br>对于每种限定符类型，仅支持一个值。例如，若要对西班牙语和法语使用相同的可绘制对象文件，则您肯定不能拥有名为 drawable-rES-rFR/ 的目录，而是需要两个包含相应文件的资源目录，如 drawable-rES/ 和 drawable-rFR/。然而，实际上您无需将相同的文件都复制到这两个位置。相反，您可以创建指向资源的别名。 请参阅下面的创建别名资源。<br>将备用资源保存到以这些限定符命名的目录中之后，Android 会根据当前设备配置在应用中自动应用这些资源。 每次请求资源时，Android 都会检查备用资源目录是否包含所请求的资源文件，然后查找最佳匹配资源（下文进行介绍）。 如果没有与特定设备配置匹配的备用资源，则 Android 会使用相应的默认资源（一组用于不含配置限定符的特定资源类型的资源）。</p>
<p>创建别名资源<br>如果您想将某一资源用于多种设备配置（但是不想作为默认资源提供），则无需将同一资源放入多个备用资源目录中。 相反，您可以（在某些情况下）创建备用资源，充当保存在默认资源目录下的资源的别名。</p>
<p>注：并非所有资源都会提供相应机制让您创建指向其他资源的别名。 特别是，xml/ 目录中的动画资源、菜单资源、原始资源以及其他未指定资源均不提供此功能。</p>
<p>例如，假设您有一个应用图标 icon.png，并且需要不同语言区域的独特版本。 但是，加拿大英语和加拿大法语这两种语言区域需要使用同一版本。 您可能会认为需要将相同的图像复制到加拿大英语和加拿大法语对应的资源目录中，但事实并非如此。 相反，您可以将用于二者的图像另存为 icon_ca.png（除 icon.png 以外的任何名称），并将其放入默认 res/drawable/ 目录中。然后，在 res/drawable-en-rCA/ 和 res/drawable-fr-rCA/ 中创建 icon.xml 文件，使用 <bitmap> 元素引用 icon_ca.png 资源。这样，您只需存储 PNG 文件的一个版本和两个指向该版本的小型 XML 文件。（XML 文件示例如下。）</bitmap></p>
<p>可绘制对象<br>要创建指向现有可绘制对象的别名，请使用 <bitmap> 元素。例如：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span><br><span class="line">&lt;bitmap xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;</span><br><span class="line">    android:src=&quot;@drawable/icon_ca&quot; /&gt;</span><br></pre></td></tr></table></figure></bitmap></p>
<p>如果将此文件另存为 icon.xml（例如，在备用资源目录中，另存为 res/drawable-en-rCA/），则会编译到可作为 R.drawable.icon 引用的资源中，但实际上它是 R.drawable.icon_ca 资源（保存在 res/drawable/ 中）的别名。</p>
<p>布局<br>要创建指向现有布局的别名，请使用包装在 <merge> 中的 <include> 元素。例如：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span><br><span class="line">&lt;merge&gt;</span><br><span class="line">    &lt;include layout=&quot;@layout/main_ltr&quot;/&gt;</span><br><span class="line">&lt;/merge&gt;</span><br></pre></td></tr></table></figure></include></merge></p>
<p>如果将此文件另存为 main.xml，则会编译到可作为 R.layout.main 引用的资源中，但实际上它是 R.layout.main_ltr 资源的别名。</p>
<p>字符串和其他简单值<br>要创建指向现有字符串的别名，只需将所需字符串的资源 ID 用作新字符串的值即可。例如：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span><br><span class="line">&lt;resources&gt;</span><br><span class="line">    &lt;string name=&quot;hello&quot;&gt;Hello&lt;/string&gt;</span><br><span class="line">    &lt;string name=&quot;hi&quot;&gt;@string/hello&lt;/string&gt;</span><br><span class="line">&lt;/resources&gt;</span><br><span class="line">R.string.hi 资源现在是 R.string.hello 的别名。</span><br></pre></td></tr></table></figure></p>
<p>其他简单值的原理相同。 例如，颜色：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span><br><span class="line">&lt;resources&gt;</span><br><span class="line">    &lt;color name=&quot;red&quot;&gt;#f00&lt;/color&gt;</span><br><span class="line">    &lt;color name=&quot;highlight&quot;&gt;@color/red&lt;/color&gt;</span><br><span class="line">&lt;/resources&gt;</span><br></pre></td></tr></table></figure></p>
<p>利用资源提供最佳设备兼容性<br>要使应用支持多种设备配置，则务必为应用使用的每种资源类型提供默认资源，这一点非常重要。</p>
<p>例如，如果应用支持多种语言，请始终包含不带语言和区域限定符的 values/ 目录（用于保存字符串）。相反，如果您将所有字符串放入带有语言和区域限定符的目录中，则在语言设置不支持您的字符串的设备上运行应用时，应用将会崩溃。 但是，只要提供默认 values/ 资源，应用就会正常运行（即使用户不理解该语言，这也总比崩溃要好）。</p>
<p>同样，如果您根据屏幕方向提供不同的布局资源，则应选择一个方向作为默认方向。 例如，不要在 layout-land/ 和 layout-port/ 中分别提供横向和纵向的布局资源，而是保留其中之一作为默认设置，例如：layout/ 用于横向，layout-port/ 用于纵向。</p>
<p>提供默认资源至关重要，这不仅仅因为应用可能在超出预期的配置上运行，也因为新版 Android 有时会添加旧版本不支持的配置限定符。若要使用新的资源限定符，又希望维持对旧版 Android 的代码兼容性，则当旧版 Android 运行应用时，如果不提供默认资源，应用将会崩溃，这是因为它无法使用以新限定符命名的资源。例如，如果将 minSdkVersion 设置为 4，并使用夜间模式（night 或 notnight，API 级别 8 中新增配置）限定所有可绘制对象资源，则 API 级别 4 设备无法访问可绘制对象资源，而且会崩溃。在这种情况下，您可能希望 notnight 成为默认资源，为此，您应排除该限定符，使可绘制对象资源位于 drawable/ 或 drawable-night/ 中。</p>
<p>因此，为了提供最佳设备兼容性，请始终为应用正确运行所必需的资源提供默认资源。 然后，使用配置限定符为特定的设备配置创建备用资源。</p>
<p>这条规则有一个例外：如果应用的 minSdkVersion 为 4 或更高版本，则在提供带屏幕密度限定符的备用可绘制对象资源时，不需要默认可绘制对象资源。 即使没有默认可绘制对象资源，Android 也可以从备用屏幕密度中找到最佳匹配项并根据需要缩放位图。 但是，为了在所有类型的设备上提供最佳体验，您应该为所有三种类型的密度提供备用可绘制对象。</p>
<p>Android 如何查找最佳匹配资源<br>当您请求要为其提供备用资源的资源时，Android 会根据当前的设备配置选择要在运行时使用的备用资源。为演示 Android 如何选择备用资源，假设以下可绘制对象目录分别包含相同图像的不同版本：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">drawable/</span><br><span class="line">drawable-en/</span><br><span class="line">drawable-fr-rCA/</span><br><span class="line">drawable-en-port/</span><br><span class="line">drawable-en-notouch-12key/</span><br><span class="line">drawable-port-ldpi/</span><br><span class="line">drawable-port-notouch-12key/</span><br><span class="line">同时，假设设备配置如下：</span><br></pre></td></tr></table></figure></p>
<p>语言区域 = en-GB<br>屏幕方向 = port<br>屏幕像素密度 = hdpi<br>触摸屏类型 = notouch<br>主要文本输入法 = 12key</p>
<p>通过将设备配置与可用的备用资源进行比较，Android 从 drawable-en-port 中选择可绘制对象。</p>
<p>系统使用以下逻辑决定要使用的资源：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">图 2. Android 如何查找最佳匹配资源的流程图。</span><br><span class="line"></span><br><span class="line">淘汰与设备配置冲突的资源文件。</span><br><span class="line">drawable-fr-rCA/ 目录与 en-GB 语言区域冲突，因而被淘汰。</span><br><span class="line"></span><br><span class="line">drawable/</span><br><span class="line">drawable-en/</span><br><span class="line">drawable-fr-rCA/</span><br><span class="line">drawable-en-port/</span><br><span class="line">drawable-en-notouch-12key/</span><br><span class="line">drawable-port-ldpi/</span><br><span class="line">drawable-port-notouch-12key/</span><br></pre></td></tr></table></figure>
<p>例外：屏幕像素密度是唯一一个未因冲突而被淘汰的限定符。 尽管设备的屏幕密度为 hdpi，但是 drawable-port-ldpi/ 未被淘汰，因为此时每个屏幕密度均视为匹配。如需了解详细信息，请参阅支持多种屏幕文档。</p>
<p>选择列表（表 2）中（下一个）优先级最高的限定符。（先从 MCC 开始，然后下移。）<br>是否有资源目录包括此限定符？<br>若无，请返回到第 2 步，看看下一个限定符。（在该示例中，除非达到语言限定符，否则答案始终为“否”。）<br>若有，请继续执行第 4 步。<br>淘汰不含此限定符的资源目录。在该示例中，系统会淘汰所有不含语言限定符的目录。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">drawable/</span><br><span class="line">drawable-en/</span><br><span class="line">drawable-en-port/</span><br><span class="line">drawable-en-notouch-12key/</span><br><span class="line">drawable-port-ldpi/</span><br><span class="line">drawable-port-notouch-12key/</span><br></pre></td></tr></table></figure></p>
<p>例外：如果涉及的限定符是屏幕像素密度，则 Android 会选择最接近设备屏幕密度的选项。通常，Android 倾向于缩小大型原始图像，而不是放大小型原始图像。请参阅支持多种屏幕。</p>
<p>返回并重复第 2 步、第 3 步和第 4 步，直到只剩下一个目录为止。在此示例中，屏幕方向是下一个判断是否匹配的限定符。因此，未指定屏幕方向的资源被淘汰：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">drawable-en/</span><br><span class="line">drawable-en-port/</span><br><span class="line">drawable-en-notouch-12key/</span><br><span class="line">剩下的目录是 drawable-en-port。</span><br></pre></td></tr></table></figure></p>
<p>尽管对所请求的每个资源均执行此程序，但是系统仍会对某些方面做进一步优化。 例如，系统一旦知道设备配置，即会淘汰可能永远无法匹配的备用资源。 比如说，如果配置语言是英语（“en”），则系统绝不会将语言限定符设置为非英语的任何资源目录包含在选中的资源池中（不过，仍会将不带语言限定符的资源目录包含在该池中）。</p>
<p>根据屏幕尺寸限定符选择资源时，如果没有更好的匹配资源，则系统将使用专为小于当前屏幕的屏幕而设计的资源（例如，如有必要，大尺寸屏幕将使用标准尺寸的屏幕资源）。 但是，如果唯一可用的资源大于当前屏幕，则系统不会使用这些资源，并且如果没有其他资源与设备配置匹配，应用将会崩溃（例如，如果所有布局资源均用 xlarge 限定符标记，但设备是标准尺寸的屏幕）。</p>
<p>注：限定符的优先顺序（表 2 中）比与设备完全匹配的限定符数量更加重要。例如，在上面的第 4 步中，列表剩下的最后选项包括三个与设备完全匹配的限定符（方向、触摸屏类型和输入法），而 drawable-en 只有一个匹配参数（语言）。但是，语言的优先顺序高于其他两个限定符，因此 drawable-port-notouch-12key 被淘汰。</p>
<p>如需了解有关如何在应用中使用资源的更多信息，请转至访问资源。</p>
<p>处理运行时变更<br>有些设备配置可能会在运行时发生变化（例如屏幕方向、键盘可用性及语言）。 发生这种变化时，Android 会重启正在运行的 Activity（先后调用 onDestroy() 和 onCreate()）。重启行为旨在通过利用与新设备配置匹配的备用资源自动重新加载您的应用，来帮助它适应新配置。</p>
<p>要妥善处理重启行为，Activity 必须通过常规的Activity 生命周期恢复其以前的状态，在 Activity 生命周期中，Android 会在销毁 Activity 之前调用 onSaveInstanceState()，以便您保存有关应用状态的数据。 然后，您可以在 onCreate() 或 onRestoreInstanceState() 期间恢复 Activity 状态。</p>
<p>要测试应用能否在保持应用状态完好的情况下自行重启，您应该在应用中执行各种任务时调用配置变更（例如，更改屏幕方向）。 您的应用应该能够在不丢失用户数据或状态的情况下随时重启，以便处理如下事件：配置发生变化，或者用户收到来电并在应用进程被销毁很久之后返回到应用。 要了解如何恢复 Activity 状态，请阅读 Activity 生命周期。</p>
<p>但是，您可能会遇到这种情况：重启应用并恢复大量数据不仅成本高昂，而且给用户留下糟糕的使用体验。 在这种情况下，您有两个其他选择：</p>
<p>在配置变更期间保留对象<br>允许 Activity 在配置变更时重启，但是要将有状态对象传递给 Activity 的新实例。</p>
<h2 id="自行处理配置变更"><a href="#自行处理配置变更" class="headerlink" title="自行处理配置变更"></a>自行处理配置变更</h2><p>阻止系统在某些配置变更期间重启 Activity，但要在配置确实发生变化时接收回调，这样，您就能够根据需要手动更新 Activity。</p>
<p>在配置变更期间保留对象<br>如果重启 Activity 需要恢复大量数据、重新建立网络连接或执行其他密集操作，那么因配置变更而引起的完全重启可能会给用户留下应用运行缓慢的体验。 此外，依靠系统通过onSaveInstanceState() 回调为您保存的 Bundle，可能无法完全恢复 Activity 状态，因为它并非设计用于携带大型对象（例如位图），而且其中的数据必须先序列化，再进行反序列化，这可能会消耗大量内存并使得配置变更速度缓慢。 在这种情况下，如果 Activity 因配置变更而重启，则可通过保留 Fragment 来减轻重新初始化 Activity 的负担。此片段可能包含对您要保留的有状态对象的引用。</p>
<p>当 Android 系统因配置变更而关闭 Activity 时，不会销毁您已标记为要保留的 Activity 的片段。 您可以将此类片段添加到 Activity 以保留有状态的对象。</p>
<p>要在运行时配置变更期间将有状态的对象保留在片段中，请执行以下操作：</p>
<p>扩展 Fragment 类并声明对有状态对象的引用。<br>在创建片段后调用 setRetainInstance(boolean)。<br>将片段添加到 Activity。<br>重启 Activity 后，使用 FragmentManager 检索片段。<br>例如，按如下方式定义片段：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">public class RetainedFragment extends Fragment &#123;</span><br><span class="line"></span><br><span class="line">    // data object we want to retain</span><br><span class="line">    private MyDataObject data;</span><br><span class="line"></span><br><span class="line">    // this method is only called once for this fragment</span><br><span class="line">    @Override</span><br><span class="line">    public void onCreate(Bundle savedInstanceState) &#123;</span><br><span class="line">        super.onCreate(savedInstanceState);</span><br><span class="line">        // retain this fragment</span><br><span class="line">        setRetainInstance(true);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void setData(MyDataObject data) &#123;</span><br><span class="line">        this.data = data;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public MyDataObject getData() &#123;</span><br><span class="line">        return data;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>注意：尽管您可以存储任何对象，但是切勿传递与 Activity 绑定的对象，例如，Drawable、Adapter、View 或其他任何与 Context 关联的对象。否则，它将泄漏原始 Activity 实例的所有视图和资源。 （泄漏资源意味着应用将继续持有这些资源，但是无法对其进行垃圾回收，因此可能会丢失大量内存。）</p>
<p>然后，使用 FragmentManager 将片段添加到 Activity。在运行时配置变更期间再次启动 Activity 时，您可以获得片段中的数据对象。 例如，按如下方式定义 Activity：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">public class MyActivity extends Activity &#123;</span><br><span class="line"></span><br><span class="line">    private RetainedFragment dataFragment;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void onCreate(Bundle savedInstanceState) &#123;</span><br><span class="line">        super.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.main);</span><br><span class="line"></span><br><span class="line">        // find the retained fragment on activity restarts</span><br><span class="line">        FragmentManager fm = getFragmentManager();</span><br><span class="line">        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);</span><br><span class="line"></span><br><span class="line">        // create the fragment and data the first time</span><br><span class="line">        if (dataFragment == null) &#123;</span><br><span class="line">            // add the fragment</span><br><span class="line">            dataFragment = new DataFragment();</span><br><span class="line">            fm.beginTransaction().add(dataFragment, “data”).commit();</span><br><span class="line">            // load the data from the web</span><br><span class="line">            dataFragment.setData(loadMyData());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        // the data is available in dataFragment.getData()</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void onDestroy() &#123;</span><br><span class="line">        super.onDestroy();</span><br><span class="line">        // store the data in the fragment</span><br><span class="line">        dataFragment.setData(collectMyLoadedData());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>在此示例中，onCreate() 添加了一个片段或恢复了对它的引用。此外，onCreate() 还将有状态的对象存储在片段实例内部。onDestroy() 对所保留的片段实例内的有状态对象进行更新。</p>
<p>自行处理配置变更<br>如果应用在特定配置变更期间无需更新资源，并且因性能限制您需要尽量避免重启，则可声明 Activity 将自行处理配置变更，这样可以阻止系统重启 Activity。</p>
<p>注：自行处理配置变更可能导致备用资源的使用更为困难，因为系统不会为您自动应用这些资源。 只能在您必须避免 Activity 因配置变更而重启这一万般无奈的情况下，才考虑采用自行处理配置变更这种方法，而且对于大多数应用并不建议使用此方法。</p>
<p>要声明由 Activity 处理配置变更，请在清单文件中编辑相应的 <activity> 元素，以包含 android:configChanges 属性以及代表要处理的配置的值。android:configChanges 属性的文档中列出了该属性的可能值（最常用的值包括 “orientation” 和 “keyboardHidden”，分别用于避免因屏幕方向和可用键盘改变而导致重启）。您可以在该属性中声明多个配置值，方法是用管道 | 字符分隔这些配置值。</activity></p>
<p>例如，以下清单文件代码声明的 Activity 可同时处理屏幕方向变更和键盘可用性变更：</p>
<activity android:name=".MyActivity" android:configchanges="orientation|keyboardHidden" android:label="@string/app_name">    

<p>现在，当其中一个配置发生变化时，MyActivity 不会重启。相反，MyActivity 会收到对 onConfigurationChanged() 的调用。向此方法传递 Configuration 对象指定新设备配置。您可以通过读取 Configuration 中的字段，确定新配置，然后通过更新界面中使用的资源进行适当的更改。调用此方法时，Activity 的 Resources 对象会相应地进行更新，以根据新配置返回资源，这样，您就能够在系统不重启 Activity 的情况下轻松重置 UI 的元素。</p>
<p>注意：从 Android 3.2（API 级别 13）开始，当设备在纵向和横向之间切换时，“屏幕尺寸”也会发生变化。因此，在开发针对 API 级别 13 或更高版本（正如 minSdkVersion 和 targetSdkVersion 属性中所声明）的应用时，若要避免由于设备方向改变而导致运行时重启，则除了 “orientation” 值以外，您还必须添加 “screenSize” 值。 也就是说，您必须声明 android:configChanges=”orientation|screenSize”。但是，如果您的应用面向 API 级别 12 或更低版本，则 Activity 始终会自行处理此配置变更（即便是在 Android 3.2 或更高版本的设备上运行，此配置变更也不会重启 Activity）。</p>
<p>例如，以下 onConfigurationChanged() 实现检查当前设备方向：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public void onConfigurationChanged(Configuration newConfig) &#123;</span><br><span class="line">   super.onConfigurationChanged(newConfig);</span><br><span class="line">   // Checks the orientation of the screen</span><br><span class="line">   if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) &#123;</span><br><span class="line">   Toast.makeText(this, &quot;landscape&quot;, Toast.LENGTH_SHORT).show();</span><br><span class="line">   &#125; else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)&#123;</span><br><span class="line">   Toast.makeText(this, &quot;portrait&quot;, Toast.LENGTH_SHORT).show();</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Configuration 对象代表所有当前配置，而不仅仅是已经变更的配置。大多数时候，您并不在意配置具体发生了哪些变更，而且您可以轻松地重新分配所有资源，为您正在处理的配置提供备用资源。 例如，由于 Resources 对象现已更新，因此您可以通过 setImageResource() 重置任何 ImageView，并且使用适合于新配置的资源（如提供资源中所述）。</p>
<p>请注意，Configuration 字段中的值是与 Configuration 类中的特定常量匹配的整型数。有关要对每个字段使用哪些常量的文档，请参阅 Configuration 参考文档中的相应字段。</p>
<p>请谨记：在声明由 Activity 处理配置变更时，您有责任重置要为其提供备用资源的所有元素。 如果您声明由 Activity 处理方向变更，而且有些图像应该在横向和纵向之间切换，则必须在 onConfigurationChanged() 期间将每个资源重新分配给每个元素。</p>
<p>如果无需基于这些配置变更更新应用，则可不用实现 onConfigurationChanged()。在这种情况下，仍将使用在配置变更之前用到的所有资源，只是您无需重启 Activity。 但是，应用应该始终能够在保持之前状态完好的情况下关闭和重启，因此您不得试图通过此方法来逃避在正常 Activity 生命周期期间保持您的应用状态。 这不仅仅是因为还存在其他一些无法禁止重启应用的配置变更，还因为有些事件必须由您处理，例如用户离开应用，而在用户返回应用之前该应用已被销毁。</p>
<p>如需了解有关您可以在 Activity 中处理哪些配置变更的详细信息，请参阅 android:configChanges 文档和 Configuration 类。</p>
<h2 id="本地化您的应用"><a href="#本地化您的应用" class="headerlink" title="本地化您的应用"></a>本地化您的应用</h2><p>Android可在许多地区的许多设备上运行。为了覆盖大多数用户，您的应用应以适合使用您的应用的区域设置的方式处理文本，音频文件，数字，货币和图形。</p>
<p>本文档介绍了本地化Android应用的最佳做法。</p>
<p>您应具备Java编程语言的工作知识，熟悉Android资源加载，XML中用户界面元素的 声明，活动生命周期等开发注意事项以及国际化和本地化的一般原则。</p>
<p>尽量使用Android资源框架将应用程序的本地化方面与核心的基于Java的功能分开，这是一个很好的做法：</p>
<p>您可以将应用程序用户界面的大部分或全部内容放入资源文件中，如本文档和 提供资源中所述。<br>另一方面，用户界面的行为是由基于Java的代码驱动的。例如，如果用户根据语言环境输入需要格式化或排序不同的数据，则可以使用Java编程语言以编程方式处理数据。本文档不包括如何本地化您的基于Java的代码。<br>有关在您的应用中本地化字符串的简短指南，请参阅培训课程， 支持不同的语言。</p>
<p>概述：Android中的资源切换<br>资源是文本字符串，布局，声音，图形以及您的Android应用所需的任何其他静态数据。应用程序可以包含多组资源，每组资源都针对不同的设备配置进行了自定义。当用户运行应用程序时，Android会自动选择并加载与设备最匹配的资源。</p>
<p>（本文档重点介绍本地化和区域设置，有关资源切换和您可以指定的所有配置类型（屏幕方向，触摸屏类型等）的完整说明，请参阅 提供替代资源。</p>
<p>当您编写应用程序时，可以为您的应用程序创建默认和替代资源以供使用。当用户运行您的应用时，Android系统会根据设备的区域设置选择要加载的资源。要创建资源，请将文件放置在项目res/目录的特定子目录中。</p>
<p>为什么默认资源很重要<br>每当应用程序在您未提供区域设置特定文本的语言环境中运行时，Android会从中加载默认字符串 res/values/strings.xml。如果此缺省文件不存在，或者缺少应用程序需要的字符串，那么您的应用程序不会运行并显示错误。下面的例子说明了默认文本文件不完整时会发生什么。</p>
<p>例：</p>
<p>应用程序的基于Java的代码仅涉及两个字符串，text_a 而且text_b。这个程序包括一个本地化的资源文件（res/values-en/strings.xml定义）text_a和 text_b英语。此应用程序还包含一个默认资源文件（res/values/strings.xml），其中包含一个定义text_a，但不包括text_b：</p>
<p>当此应用在设置为英语的语言环境设备上启动时，该应用可能无问题地运行，因为它 res/values-en/strings.xml包含两个所需的文本字符串。<br>但是，如果在设置为英语以外的语言的设备上启动此应用程序，用户将看到错误消息和强制关闭按钮。该应用程序无法加载。<br>为防止出现这种情况，请确保res/values/strings.xml 文件存在，并确定每个需要的字符串。这种情况适用于所有类型的资源，而不仅仅是字符串：您需要创建一组默认资源文件，其中包含应用程序调用的所有资源 - 布局，可绘制，动画等。有关测试的信息，请参阅 测试默认值资源。</p>
<p>使用资源进行本地化<br>如何创建默认资源<br>将应用程序的默认文本放入 res/values/strings.xml。</p>
<p>文本字符串res/values/strings.xml应使用默认语言，这是您希望大多数应用用户说话的语言。</p>
<p>默认资源集还必须包含任何默认的可绘制和布局，并且可以包含其他类型的资源，如动画：</p>
<p>res/drawable/（必需的目录至少保存一个图形文件，用于Google Play上应用的图标）<br>res/layout/ （需要保存定义默认布局的XML文件的目录）<br>res/anim/（如果您有任何 文件夹，则需要）res/anim-<qualifiers><br>res/xml/（如果您有任何 文件夹，则需要）res/xml-<qualifiers><br>res/raw/（如果您有任何 文件夹，则需要）res/raw-<qualifiers><br>提示：在您的代码中，检查每个对Android资源的引用。确保为每个资源定义了一个默认资源。还要确保默认的字符串文件是完整的：本地化的 字符串文件可以包含字符串的一个子集，但是默认的字符串文件必须包含它们全部。</qualifiers></qualifiers></qualifiers></p>
<p>如何创建替代资源<br>本地化应用程序的很大一部分是为不同语言提供替代文本。在某些情况下，您还可以提供其他图形，声音，布局和其他特定于语言环境的资源。</p>
<p>应用程序可以指定许多 目录，每个目录都有不同的限定符。要为不同的区域设置创建替代资源，请使用指定语言或语言区域组合的限定符。（资源目录的名称必须符合Provide Alternative Resources中所述的命名方案 ，否则您的应用程序无法编译。）res/<qualifiers>/</qualifiers></p>
<p>例：</p>
<p>假设你的应用的默认语言是英文。假设您还想将应用中的所有文本本地化为法文，并将您应用中的大部分文本（除应用标题外的所有文本）本地化为日文。在这种情况下，您可以创建三个备选strings.xml 文件，每个文件都存储在特定于语言环境的资源目录中：</p>
<p>res/values/strings.xml<br>包含应用程序使用的所有字符串的英文文本，包括名为字符串的文本title。<br>res/values-fr/strings.xml<br>包含所有字符串的法文文本，包括title。<br>res/values-ja/strings.xml<br>包含所有字符串除外的 日文文本title。<br>如果您的基于Java的代码引用R.string.title，那么在运行时会发生以下情况：</p>
<p>如果设备设置为法语以外的任何语言，则Android会title从该res/values/strings.xml文件加载 。<br>如果设备设置为法语，则Android会title从该 res/values-fr/strings.xml文件加载。<br>请注意，如果设备设置为日语，Android会title在res/values-ja/strings.xml文件中查找 。但是由于该文件中不包含此类字符串，Android会回退到默认值，并title从res/values/strings.xml文件加载英文 。</p>
<p>哪些资源优先？<br>如果多个资源文件与设备的配置相匹配，则Android会根据一组规则来决定要使用哪个文件。在可以在资源目录名称中指定的限定符中，语言环境几乎总是优先。</p>
<p>例：</p>
<p>假设一个应用程序包含一组默认图形和另外两组图形，每个图形均针对不同的设备设置进行了优化：</p>
<p>res/drawable/<br>包含默认图形。<br>res/drawable-small-land-stylus/<br>包含经过优化的图形，用于预期来自手写笔输入的设备，并且具有横向方向的QVGA低密度屏幕。<br>res/drawable-ja/<br>包含针对日语使用而优化的图形。<br>如果应用程序在配置为使用日语的设备上运行，则Android会加载图形res/drawable-ja/，即使该设备恰好是需要从触控笔输入的设备，并且横向上具有QVGA低密度屏幕。</p>
<p>例外：在选择过程中唯一的优先于区域设置的限定符是MCC和MNC（移动国家代码和移动网络代码）。</p>
<p>例：</p>
<p>假设您有以下情况：</p>
<p>应用程序代码要求 R.string.text_a<br>有两个相关的资源文件可用：<br>res/values-mcc404/strings.xml，其中包含 text_a应用的默认语言，在这种情况下为英文。<br>res/values-hi/strings.xml，其中包括 text_a印地文。<br>该应用程序在具有以下配置的设备上运行：<br>SIM卡连接到印度的移动网络（MCC 404）。<br>语言设置为Hindi（hi）。<br>即使设备配置为印地语，Android 也会text_a从 res/values-mcc404/strings.xml（英语）加载。这是因为在资源选择过程中，Android更喜欢MCC匹配的语言匹配。</p>
<p>选择过程并不总是像这些例子所表明的那样简单。请阅读Android如何找到最佳匹配资源，以获取更详细的流程描述。所有的限定符都按照提供替代资源表2中的优先顺序进行了描述和列出。</p>
<p>参考代码中的资源<br>在您的应用程序的基于Java的代码中，您可以使用语法 或 。 有关更多信息，请参阅访问资源。R.resource_type.resource_nameandroid.R.resource_type.resource_name</p>
<p>管理本地化字符串<br>将所有字符串移动到strings.xml中<br>在构建应用程序时，不要硬编码任何字符串。相反，将所有字符串声明为默认strings.xml文件中的资源，这使得更新和本地化变得容易。strings.xml然后可以轻松提取，翻译文件中的字符串并将其集成到应用程序中（具有适当的限定符），而不会对编译代码进行任何更改。</p>
<p>如果您使用文本生成图像，请将这些字符串放入strings.xml，并在翻译后重新生成图像。</p>
<p>遵循Android用户界面字符串指南<br>当你设计和开发的用户界面，请确保您密切关注如何你跟你的用户。一般来说，使用简洁且简洁的简洁压缩样式，并在整个UI中使用一致的样式。</p>
<p>确保您阅读并遵循材料设计建议书写风格和文字选择。这样做可以让您的应用看起来更加精美，并帮助用户更快地理解您的用户界面。</p>
<p>此外，尽可能使用Android标准术语，例如操作栏，选项菜单，系统栏，通知等UI元素。正确和一致地使用Android术语可以使翻译更轻松，并为用户带来更好的最终产品。</p>
<p>为声明字符串提供足够的上下文<br>在您的strings.xml文件中声明字符串时，请确保描述使用字符串的上下文。这些信息对翻译非常重要，并可以提高翻译质量。它还可以帮助您更有效地管理您的字符串。</p>
<p>这里是一个例子：</p>
<p>&lt;！ - 提交表单的动作。此文本位于可放置30个字符的按钮上 - &gt; <string name="“login_submit_button”"> 登录&lt;/ string&gt;</string></p>
<p>考虑提供上下文信息，其中可能包括：</p>
<p>这是什么字符串？何时何地呈现给用户？<br>这是在布局？例如，按钮中的翻译比文本框中的翻译更不灵活。<br>标记不应该翻译的消息部分<br>字符串常常包含不应该翻译成其他语言的文本。常见示例可能是一段代码，一个值的占位符，一个特殊符号或名称。在准备翻译字符串时，请查找并标记应保持原样的文本，而无需翻译，以便翻译人员不会更改它。</p>
<p>要标记不应翻译的文本，请使用<a href="xliff:g" target="_blank" rel="noopener">xliff:g</a> 占位符标记。下面是一个示例标签，可确保在翻译过程中文本“％1 $ s”不会更改（否则可能会中断该消息）：</p>
<p><string name="“countdown”"> &lt;xliff：g id = “time” example = “5 days” &gt; ％1 $ s &lt;/ xliff：g&gt; until holiday &lt;/ string&gt; </string></p>
<p>当您声明占位符标记时，请始终添加一个id属性，以说明占位符的用途。如果您的应用程序稍后替换了占位符值，请确保提供示例属性以阐明预期用途。</p>
<p>以下是一些占位符标签的例子：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;resources xmlns：xliff = “urn：oasis：names：tc：xliff：document：1.2” &gt; &lt;！ - 一个特殊的unicode符号的示例占位符 - &gt; &lt;string name = “star_rating” &gt; 查看我们的5 &lt;xliff ：摹ID = “明星” &gt; \ u2605 &lt;/ XLIFF：g&gt;的&lt;/字符串&gt; &lt;！ -示例占位符一个一个网址- &gt; &lt;字符串名称= “app_homeurl” &gt;     请访问我们的&lt;XLIFF：摹ID = “application_homepage” &gt; http：//my/app/home.html &lt;/ xliff：g&gt;&lt;/字符串&gt;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;！ - 名称示例占位符 - &gt; &lt;string name = “prod_name” &gt;     在&lt;xliff：g id = “prod_gamegroup” &gt; Game Group &lt;/ xliff：g&gt; &lt;/ string&gt; &lt;！ - placeholder for a literal  - &gt; &lt;string name = “promo_message” &gt;     请使用“ &lt;xliff：g id = ”promotion_code“ &gt; ABCDEFG &lt;/ xliff：g&gt; ”来获得折扣。&lt;/ string&gt; ... &lt;/ resources&gt;</span><br></pre></td></tr></table></figure>
<p>本地化清单<br>有关本地化和分发Android应用程序的完整概述，请参阅本地化核对表文档。</p>
<p>本地化提示<br>设计您的应用程序以在任何语言环境中工作<br>您不能假设用户运行您的应用的设备。该设备可能具有您未预期的硬件，或者可能设置为您不打算或者无法测试的语言环境。设计您的应用程序，使其正常运行或无论其运行在哪个设备上都会优雅地失败。</p>
<p>重要提示：确保您的应用包含一整套默认资源。</p>
<p>确保包含 res/drawable/和res/values/文件夹（文件夹名称中没有任何其他修饰符），其中包含您的应用程序需要的所有图像和文本。</p>
<p>如果某个应用程序即使缺少一个默认资源，也不会在设置为不受支持的区域设置的设备上运行。例如， res/values/strings.xml默认文件可能缺少应用程序需要的一个字符串：当应用程序在不支持的语言环境中运行并尝试加载时res/values/strings.xml，用户会看到一条错误消息和一个强制关闭按钮。</p>
<p>有关更多信息，请参阅测试默认资源。</p>
<p>设计灵活的布局<br>如果您需要重新排列布局以适合特定语言（例如，德语的长词），则可以为该语言创建替代布局（例如res/layout-de/main.xml）。但是，这样做可能会让您的应用难以维护。最好创建一个更灵活的单一布局。</p>
<p>另一种典型情况是需要不同布局的语言。例如，您可能有一个联系表单，当应用程序以日语运行时应该包含两个名称字段，而当应用程序以其他语言运行时，应该包含三个名称字段。您可以通过以下两种方式之一处理：</p>
<p>根据语言或者可以编程启用或禁用的字段创建一个布局<br>主布局包含另一个包含可更改字段的布局。第二个布局可以针对不同的语言有不同的配置。<br>避免创建比您需要更多的资源文件和文本字符串<br>您可能不需要为应用中的每个资源创建特定于语言环境的替代方案。例如，res/layout/main.xml文件中定义的布局 可能在任何语言环境中工作，在这种情况下，不需要创建任何替代布局文件。</p>
<p>另外，您可能不需要为每个字符串创建替代文本。例如，假设如下：</p>
<p>您应用的默认语言是美式英语。应用程序使用的每个字符串都是使用美国英语拼写定义的 res/values/strings.xml。<br>对于一些重要的短语，你想提供英式英语拼写。当您的应用在英国的设备上运行时，您希望使用这些替代字符串。<br>为此，您可以创建一个名为的小文件 res/values-en-rGB/strings.xml，其中仅包含应用程序在英国运行时应该不同的字符串。对于所有其他字符串，应用程序将回退到默认值并使用其中定义的内容res/values/strings.xml。</p>
<p>使用Android Context对象进行手动区域设置查找<br>您可以使用ContextAndroid提供的对象查找语言环境：</p>
<p>字符串区域设置= 上下文。getResources （）。getConfiguration （）。区域设置。getDisplayName （）;<br>使用应用翻译服务<br>该应用翻译服务被整合到游戏控制台，它也是从访问Android的工作室。这是一种快速简便的方式，可以即时报价并向翻译公司下订单。您可以为应用UI字符串，Play商品详情文本，IAP名称和广告活动文本订购翻译为一种或多种语言。</p>
<p>测试本地化应用<br>在设备上测试<br>请记住，您正在测试的设备可能与其他地区的消费者可用的设备有很大不同。您设备上可用的区域设置可能与其他设备上可用的区域设置不同。此外，设备屏幕的分辨率和密度可能会有所不同，这可能会影响UI中字符串和绘图的显示。</p>
<p>要更改设备上的区域设置或语言，请使用设置应用程序。</p>
<p>在模拟器上测试<br>有关使用模拟器的详细信息，请参阅Android模拟器。</p>
<p>创建和使用自定义区域设置<br>“自定义”区域设置是Android系统图像未明确支持的语言/区域组合。您可以通过在模拟器中创建自定义语言环境来测试应用程序在自定义语言环境中的运行方式。有两种方法可以做到这一点：</p>
<p>使用可从应用程序选项卡访问的自定义区域设置应用程序。（创建自定义区域设置后，通过按住区域名称来切换到该区域。）<br>如下所述，从adb shell更改为自定义区域设置。<br>当您将模拟器设置为Android系统映像中不可用的语言环境时，系统本身将以其默认语言显示。但是，您的应用应该正确定位。</p>
<p>从adb shell更改模拟器语言环境<br>通过使用adb shell来更改模拟器中的区域设置。</p>
<p>选择您想要测试的地区并确定其BCP-47语言标签，例如，加拿大法语将是fr-CA。<br>启动一个模拟器。<br>从主机上的命令行shell中运行以下命令：<br>adb shell<br>或者如果您连接了设备，请通过添加以下-e选项来指定您希望仿真器：<br>adb -e shell<br>在adb shell提示符（#）下，运行以下命令：用步骤1中的相应代码替换括号内的部分。<br>setprop persist.sys.locale [BCP-47 language tag];stop;sleep 5;start<br>例如，要用加拿大法语进行测试：</p>
<p>setprop persist.sys.locale fr-CA;stop;sleep 5;start</p>
<p>这会导致模拟器重新启动。（它看起来像是完全重新启动，但不是。）主屏幕再次出现后，重新启动您的应用程序，并使用新的区域设置启动应用程序。</p>
<p>测试默认资源<br>以下是如何测试应用程序是否包含所需的每个字符串资源：</p>
<p>将模拟器或设备设置为您的应用不支持的语言。例如，如果应用程序中有法文字符串 res/values-fr/但没有任何西班牙字符串 res/values-es/，则将模拟器的区域设置为西班牙语。（您可以使用自定义语言环境应用程序将模拟器设置为不支持的语言环境。）<br>运行应用程序。<br>如果应用程序显示错误消息和强制关闭按钮，则可能正在查找不可用的字符串。确保您的 res/values/strings.xml文件包含应用程序使用的每个字符串的定义。<br>如果测试成功，请将其重复用于其他类型的配置。例如，如果应用程序具有调用的布局文件， res/layout-land/main.xml但不包含调用的文件 res/layout-port/main.xml，则将模拟器或设备设置为纵向方向并查看应用程序是否运行。</p>
<h2 id="乱七八糟"><a href="#乱七八糟" class="headerlink" title="乱七八糟"></a>乱七八糟</h2></activity>
      
    </div>
    
    
    

      <div>
        
          <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>
        
      </div>

    

      <div>
        <div id="wechat_subscriber" style="display: block; padding: 10px 0; margin: 20px auto; width: 100%; text-align: center">
    <img id="wechat_subscriber_qcode" src="/images/微信公众号.jpg" alt="达叔小生 wechat" style="width: 200px; max-width: 100%">
    <div>欢迎您扫一扫上面的微信公众号，订阅我的博客！</div>
</div>

      </div>
    

    
      <div>
        <div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center">
  <div>坚持原创技术分享，您的支持将鼓励我继续创作！</div>
  <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
    <span>打赏</span>
  </button>
  <div id="QR" style="display: none">

    
      <div id="wechat" style="display: inline-block">
        <img id="wechat_qr" src="/images/wechatpay.png" alt="达叔小生 微信支付">
        <p>微信支付</p>
      </div>
    

    

    

  </div>
</div>

      </div>
    

    
      <div>
        <ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者：</strong>
    达叔小生
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://huangguangda.github.io/2018/05/01/1/" title="Android应用基础知识">https://huangguangda.github.io/2018/05/01/1/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>
    本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/" rel="external nofollow" target="_blank">CC BY-NC-SA 3.0</a> 许可协议。转载请注明出处！
  </li>
</ul>

      </div>
    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Developers/" rel="tag"><i class="fa fa-tag"></i> Developers</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2018/04/28/1/" rel="next" title="教育系统APP(八)">
                <i class="fa fa-chevron-left"></i> 教育系统APP(八)
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2018/05/02/1/" rel="prev" title="JAVA语言-985大学">
                JAVA语言-985大学 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
        
<script>
  with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='//bdimg.share.baidu.com/static/api/js/share.js?cdnversion='+~(-new Date()/36e5)];
</script>

      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
      <div id="lv-container" data-id="city" data-uid="MTAyMC8zNTU5OC8xMjEzNA=="></div>
    </div>

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
      <div id="sidebar-dimmer"></div>
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image" src="/images/avatar.gif" alt="达叔小生">
            
              <p class="site-author-name" itemprop="name">达叔小生</p>
              <p class="site-description motion-element" itemprop="description"></p>

            <div id="days"></div>
<script>
function show_date_time(){
window.setTimeout("show_date_time()", 1000);
BirthDay=new Date("04/23/1997 00:00:00");
today=new Date();
timeold=(today.getTime()-BirthDay.getTime());
sectimeold=timeold/1000
secondsold=Math.floor(sectimeold);
msPerDay=24*60*60*1000
e_daysold=timeold/msPerDay
daysold=Math.floor(e_daysold);
e_hrsold=(e_daysold-daysold)*24;
hrsold=setzero(Math.floor(e_hrsold));
e_minsold=(e_hrsold-hrsold)*60;
minsold=setzero(Math.floor((e_hrsold-hrsold)*60));
seconds=setzero(Math.floor((e_minsold-minsold)*60));
document.getElementById('days').innerHTML="已运行"+daysold+"天"+hrsold+"小时"+minsold+"分"+seconds+"秒";
}
function setzero(i){
if (i<10)
{i="0" + i};
return i;
}
show_date_time();
</script>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives">
              
                  <span class="site-state-item-count">94</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">20</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">21</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
                <a title="收藏到书签，偶尔High一下^_^" rel="alternate" class="mw-harlem_shake_slow wobble shake" href="javascript:(function() {
    function c() {
        var e = document.createElement(&quot;link&quot;);
        e.setAttribute(&quot;type&quot;, &quot;text/css&quot;);
        e.setAttribute(&quot;rel&quot;, &quot;stylesheet&quot;);
        e.setAttribute(&quot;href&quot;, f);
        e.setAttribute(&quot;class&quot;, l);
        document.body.appendChild(e)
    }
 
    function h() {
        var e = document.getElementsByClassName(l);
        for (var t = 0; t < e.length; t++) {
            document.body.removeChild(e[t])
        }
    }
 
    function p() {
        var e = document.createElement(&quot;div&quot;);
        e.setAttribute(&quot;class&quot;, a);
        document.body.appendChild(e);
        setTimeout(function() {
            document.body.removeChild(e)
        }, 100)
    }
 
    function d(e) {
        return {
            height : e.offsetHeight,
            width : e.offsetWidth
        }
    }
 
    function v(i) {
        var s = d(i);
        return s.height > e && s.height < n && s.width > t && s.width < r
    }
 
    function m(e) {
        var t = e;
        var n = 0;
        while (!!t) {
            n += t.offsetTop;
            t = t.offsetParent
        }
        return n
    }
 
    function g() {
        var e = document.documentElement;
        if (!!window.innerWidth) {
            return window.innerHeight
        } else if (e && !isNaN(e.clientHeight)) {
            return e.clientHeight
        }
        return 0
    }
 
    function y() {
        if (window.pageYOffset) {
            return window.pageYOffset
        }
        return Math.max(document.documentElement.scrollTop, document.body.scrollTop)
    }
 
    function E(e) {
        var t = m(e);
        return t >= w && t <= b + w
    }
 
    function S() {
        var e = document.createElement(&quot;audio&quot;);
        e.setAttribute(&quot;class&quot;, l);
        e.src = i;
        e.loop = false;
        e.addEventListener(&quot;canplay&quot;, function() {
            setTimeout(function() {
                x(k)
            }, 500);
            setTimeout(function() {
                N();
                p();
                for (var e = 0; e < O.length; e++) {
                    T(O[e])
                }
            }, 15500)
        }, true);
        e.addEventListener(&quot;ended&quot;, function() {
            N();
            h()
        }, true);
        e.innerHTML = &quot; <p>If you are reading this, it is because your browser does not support the audio element. We recommend that you get a new browser.</p> <p>&quot;;
        document.body.appendChild(e);
        e.play()
    }
 
    function x(e) {
        e.className += &quot; &quot; + s + &quot; &quot; + o
    }
 
    function T(e) {
        e.className += &quot; &quot; + s + &quot; &quot; + u[Math.floor(Math.random() * u.length)]
    }
 
    function N() {
        var e = document.getElementsByClassName(s);
        var t = new RegExp(&quot;\\b&quot; + s + &quot;\\b&quot;);
        for (var n = 0; n < e.length; ) {
            e[n].className = e[n].className.replace(t, &quot;&quot;)
        }
    }
 
    var e = 30;
    var t = 30;
    var n = 350;
    var r = 350;
    var i = &quot;//s3.amazonaws.com/moovweb-marketing/playground/harlem-shake.mp3&quot;;
    var s = &quot;mw-harlem_shake_me&quot;;
    var o = &quot;im_first&quot;;
    var u = [&quot;im_drunk&quot;, &quot;im_baked&quot;, &quot;im_trippin&quot;, &quot;im_blown&quot;];
    var a = &quot;mw-strobe_light&quot;;
    var f = &quot;//s3.amazonaws.com/moovweb-marketing/playground/harlem-shake-style.css&quot;;
    var l = &quot;mw_added_css&quot;;
    var b = g();
    var w = y();
    var C = document.getElementsByTagName(&quot;*&quot;);
    var k = null;
    for (var L = 0; L < C.length; L++) {
        var A = C[L];
        if (v(A)) {
            if (E(A)) {
                k = A;
                break
            }
        }
    }
    if (A === null) {
        console.warn(&quot;Could not find a node of the right size. Please try a different page.&quot;);
        return
    }
    c();
    S();
    var O = [];
    for (var L = 0; L < C.length; L++) {
        var A = C[L];
        if (v(A)) {
            O.push(A)
        }
    }
})()"><i class="fa fa-music"></i> High</a>

            </div>
          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/huangguangda" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="mailto:2397923107@qq.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://www.jianshu.com/u/c785ece603d1" target="_blank" title="简书">
                      
                        <i class="fa fa-fw fa-android"></i>简书</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://blog.csdn.net/qq_36232611" target="_blank" title="csdn">
                      
                        <i class="fa fa-fw fa-spinner"></i>csdn</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://www.douban.com/people/186539024/" target="_blank" title="豆瓣">
                      
                        <i class="fa fa-fw fa-bath"></i>豆瓣</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://www.zhihu.com/people/da-shu-xiao-sheng/activities" target="_blank" title="知乎">
                      
                        <i class="fa fa-fw fa-globe"></i>知乎</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://juejin.im/user/5a16e1f3f265da43128096cb" target="_blank" title="掘金">
                      
                        <i class="fa fa-fw fa-google"></i>掘金</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://weibo.com/5507262458/profile?topnav=1&wvr=6" target="_blank" title="微博">
                      
                        <i class="fa fa-fw fa-weibo"></i>微博</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzI1NTcxOTQ1Nw==&scene=124#wechat_redirect" target="_blank" title="历史">
                      
                        <i class="fa fa-fw fa-apple"></i>历史</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="http://www.cnblogs.com/dashucoding/" target="_blank" title="博客园">
                      
                        <i class="fa fa-fw fa-facebook"></i>博客园</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://gdone.gitee.io/englishpages/index.html" target="_blank" title="英文简历">
                      
                        <i class="fa fa-fw fa-globe"></i>英文简历</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://gdone.gitee.io/pages/" target="_blank" title="中文简历">
                      
                        <i class="fa fa-fw fa-heartbeat"></i>中文简历</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://gitee.com/GDone/events" target="_blank" title="码云">
                      
                        <i class="fa fa-fw fa-twitter"></i>码云</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://my.oschina.net/xiaomaomi1997/" target="_blank" title="开源中国">
                      
                        <i class="fa fa-fw fa-facebook"></i>开源中国</a>
                  </span>
                
            </div>
          

          
          
            <div class="cc-license motion-element" itemprop="license">
              <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" target="_blank">
                <img src="/images/cc-by-nc-sa.svg" alt="Creative Commons">
              </a>
            </div>
          

          
          
            <div class="links-of-blogroll motion-element links-of-blogroll-inline">
              <div class="links-of-blogroll-title">
                <i class="fa fa-fw fa-globe"></i>
                推荐阅读
              </div>
              <ul class="links-of-blogroll-list">
                
                  <li class="links-of-blogroll-item">
                    <a href="http://www.alloyteam.com/nav/" title="Web前端导航" target="_blank">Web前端导航</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://www.36zhen.com/t?id=3448" title="前端书籍资料" target="_blank">前端书籍资料</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://ife.baidu.com/" title="百度前端技术学院" target="_blank">百度前端技术学院</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://wf.uisdc.com/cn/" title="google前端开发基础" target="_blank">google前端开发基础</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.geekbang.org/" title="极客邦科技" target="_blank">极客邦科技</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://ilxdh.com/" title="龙轩导航" target="_blank">龙轩导航</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://code.giffox.com/" title="程序员导航" target="_blank">程序员导航</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://search.chongbuluo.com/" title="快搜" target="_blank">快搜</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://www.android-doc.com/training/index.html" title="Android中文API" target="_blank">Android中文API</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://developer.android.google.cn/" title="Android Developers" target="_blank">Android Developers</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.freecodecamp.cn/" title="FreeCodeCamp中文社区" target="_blank">FreeCodeCamp中文社区</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://docs.oracle.com/javase/specs/" title="Java SE规范" target="_blank">Java SE规范</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="http://liuwangshu.cn/" title="刘望舒" target="_blank">刘望舒</a>
                  </li>
                
                  <li class="links-of-blogroll-item">
                    <a href="https://www.colabug.com/author/3159ee71c93b3b4a/" title="小楼昨夜又秋风" target="_blank">小楼昨夜又秋风</a>
                  </li>
                
              </ul>

            </div>
          


        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#Android-简介"><span class="nav-number">1.</span> <span class="nav-text">Android 简介</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#构建您的第一个应用"><span class="nav-number">2.</span> <span class="nav-text">构建您的第一个应用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#创建-Android-项目"><span class="nav-number">3.</span> <span class="nav-text">创建 Android 项目</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#运行您的应用"><span class="nav-number">4.</span> <span class="nav-text">运行您的应用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#构建简单的界面"><span class="nav-number">5.</span> <span class="nav-text">构建简单的界面</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#打开布局编辑器"><span class="nav-number">6.</span> <span class="nav-text">打开布局编辑器</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#添加一个文本框"><span class="nav-number">7.</span> <span class="nav-text">添加一个文本框</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#添加一个按钮"><span class="nav-number">8.</span> <span class="nav-text">添加一个按钮</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#更改界面字符串"><span class="nav-number">9.</span> <span class="nav-text">更改界面字符串</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#用于添加新字符串的对话框"><span class="nav-number">10.</span> <span class="nav-text">用于添加新字符串的对话框</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#让文本框大小灵活调整"><span class="nav-number">11.</span> <span class="nav-text">让文本框大小灵活调整</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#运行应用"><span class="nav-number">12.</span> <span class="nav-text">运行应用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#启动另一个-Activity"><span class="nav-number">13.</span> <span class="nav-text">启动另一个 Activity</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#响应-Send-按钮"><span class="nav-number">14.</span> <span class="nav-text">响应 Send 按钮</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#构建一个-Intent"><span class="nav-number">15.</span> <span class="nav-text">构建一个 Intent</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#创建第二个-Activity"><span class="nav-number">16.</span> <span class="nav-text">创建第二个 Activity</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#添加文本视图"><span class="nav-number">17.</span> <span class="nav-text">添加文本视图</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#添加向上导航"><span class="nav-number">18.</span> <span class="nav-text">添加向上导航</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#运行应用-1"><span class="nav-number">19.</span> <span class="nav-text">运行应用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#应用基础知识"><span class="nav-number">20.</span> <span class="nav-text">应用基础知识</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#应用资料"><span class="nav-number">21.</span> <span class="nav-text">应用资料</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#自行处理配置变更"><span class="nav-number">22.</span> <span class="nav-text">自行处理配置变更</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#本地化您的应用"><span class="nav-number">23.</span> <span class="nav-text">本地化您的应用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#乱七八糟"><span class="nav-number">24.</span> <span class="nav-text">乱七八糟</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love" id="heart">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">达叔小生</span>

  
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    
      <span class="post-meta-item-text">Site words total count&#58;</span>
    
    <span title="Site words total count">357.6k</span>
  
</div>






  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Mist</a> v5.1.4</div>




<div class="theme-info">
  <div class="powered-by"></div>
  <span class="post-count">博客全站共357.6k字</span>
</div>

<div class="weixin-box">
  <div class="weixin-menu">
    <div class="weixin-hover">
      <div class="weixin-description">微信扫一扫，订阅本博客</div>
    </div>
  </div>
</div>
        
<div class="busuanzi-count">
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">
      <i class="fa fa-user">本站访客数</i>
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
      
    </span>
  

  
    <span class="site-pv">
      <i class="fa fa-eye">本站总访问量</i>
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      次
    </span>
  
</div>





  <script type="text/javascript">
    (function() {
      var hm = document.createElement("script");
      hm.src = "//tajs.qq.com/stats?sId=65809965";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
          <span id="scrollpercent"><span>0</span>%</span>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  


  



  
  









  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  

  
  
    <script type="text/javascript" src="/lib/canvas-nest/canvas-nest.min.js"></script>
  

  
  
    <script type="text/javascript" src="/lib/three/three.min.js"></script>
  

  
  
    <script type="text/javascript" src="/lib/three/three-waves.min.js"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  












  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  

  
<script>
(function(){
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
    }
    else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>


  
  

  
  
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
    </script>

    <script type="text/x-mathjax-config">
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for (i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="//cdn.bootcss.com/mathjax/2.7.1/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
  


  

  


  <script type="text/javascript" src="/lib/clipboard/clipboard.min.js"></script>
<script type="text/javascript" src="/js/src/custom.js"></script>


   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none"></canvas> 
   <script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" src="/js/src/fireworks.js"></script>


  <!-- 代码块复制功能 -->
  <script type="text/javascript" src="/js/src/clipboard.min.js"></script>  
  <script type="text/javascript" src="/js/src/clipboard-use.js"></script>
<script src="/live2dw/lib/L2Dwidget.min.js?0c58a1486de42ac6cc1c59c7d98ae887"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","tagMode":false,"debug":false,"model":{"jsonPath":"live2d-widget-model-hibiki"},"display":{"position":"right","width":75,"height":150},"mobile":{"show":false},"log":false});</script></body>
</html>